summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2012-03-01 03:31:28 -0600
committerJohn Wiegley <johnw@newartisans.com>2012-03-01 03:31:28 -0600
commit944e580825f0d9ce060b6dcdffe8990b6c2cca20 (patch)
treee2651b0df622d9b8d3831f35743e7dbd31c16897
parente2afc783db0dff1927b00dc506390353d9e3bbd2 (diff)
downloadfork-ledger-944e580825f0d9ce060b6dcdffe8990b6c2cca20.tar.gz
fork-ledger-944e580825f0d9ce060b6dcdffe8990b6c2cca20.tar.bz2
fork-ledger-944e580825f0d9ce060b6dcdffe8990b6c2cca20.zip
Refactored the notion of "the current parsing context"
-rw-r--r--src/accum.h10
-rw-r--r--src/context.h151
-rw-r--r--src/convert.cc12
-rw-r--r--src/csv.cc81
-rw-r--r--src/csv.h33
-rw-r--r--src/generate.cc10
-rw-r--r--src/global.cc7
-rw-r--r--src/journal.cc139
-rw-r--r--src/journal.h32
-rw-r--r--src/precmd.cc10
-rw-r--r--src/py_commodity.cc2
-rw-r--r--src/py_journal.cc11
-rw-r--r--src/pyinterp.cc35
-rw-r--r--src/scope.h2
-rw-r--r--src/session.cc31
-rw-r--r--src/session.h3
-rw-r--r--src/textual.cc448
-rw-r--r--src/timelog.cc15
-rw-r--r--src/timelog.h11
-rw-r--r--src/xact.cc6
-rw-r--r--src/xact.h3
-rw-r--r--test/baseline/dir-tag.test2
-rw-r--r--test/baseline/feat-check.test2
23 files changed, 598 insertions, 458 deletions
diff --git a/src/accum.h b/src/accum.h
index 349aeba9..dde93c30 100644
--- a/src/accum.h
+++ b/src/accum.h
@@ -86,10 +86,16 @@ public:
extern straccstream _accum;
extern std::ostringstream _accum_buffer;
+inline string str_helper_func() {
+ string buf = _accum_buffer.str();
+ _accum_buffer.clear();
+ _accum_buffer.str("");
+ return buf;
+}
+
#define STR(msg) \
((_accum_buffer << ACCUM(_accum << msg)), \
- _accum.clear(), \
- _accum_buffer.str())
+ _accum.clear(), str_helper_func())
} // namespace ledger
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 00000000..0533536f
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2003-2012, 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.
+ */
+
+/**
+ * @addtogroup data
+ */
+
+/**
+ * @file context.h
+ * @author John Wiegley
+ *
+ * @ingroup data
+ */
+#ifndef _CONTEXT_H
+#define _CONTEXT_H
+
+#include "utils.h"
+#include "times.h"
+
+namespace ledger {
+
+class journal_t;
+class account_t;
+class scope_t;
+
+class parse_context_t
+{
+public:
+ static const std::size_t MAX_LINE = 4096;
+
+ shared_ptr<std::istream> stream;
+
+ path pathname;
+ path current_directory;
+ journal_t * journal;
+ account_t * master;
+ scope_t * scope;
+ char linebuf[MAX_LINE + 1];
+ istream_pos_type line_beg_pos;
+ istream_pos_type curr_pos;
+ std::size_t linenum;
+ std::size_t errors;
+ std::size_t count;
+ std::size_t sequence;
+
+ explicit parse_context_t(shared_ptr<std::istream> _stream,
+ const path& cwd)
+ : stream(_stream), current_directory(cwd), master(NULL),
+ scope(NULL), linenum(0), errors(0), count(0), sequence(1) {}
+
+ parse_context_t(const parse_context_t& context)
+ : stream(context.stream),
+ pathname(context.pathname),
+ current_directory(context.current_directory),
+ journal(context.journal),
+ master(context.master),
+ scope(context.scope),
+ line_beg_pos(context.line_beg_pos),
+ curr_pos(context.curr_pos),
+ linenum(context.linenum),
+ errors(context.errors),
+ count(context.count),
+ sequence(context.sequence) {
+ std::memcpy(linebuf, context.linebuf, MAX_LINE);
+ }
+
+ string location() const {
+ return file_context(pathname, linenum);
+ }
+
+ void warning(const string& what) const {
+ warning_func(location() + what);
+ }
+};
+
+inline parse_context_t open_for_reading(const path& pathname,
+ const path& cwd)
+{
+ path filename = resolve_path(pathname);
+
+ if (! exists(filename))
+ throw_(std::runtime_error,
+ _("Cannot read journal file %1") << filename);
+
+ path parent(filesystem::absolute(pathname, cwd).parent_path());
+ shared_ptr<std::istream> stream(new ifstream(filename));
+ parse_context_t context(stream, parent);
+ context.pathname = filename;
+ return context;
+}
+
+class parse_context_stack_t
+{
+ std::list<parse_context_t> parsing_context;
+
+public:
+ void push(shared_ptr<std::istream> stream,
+ const path& cwd = filesystem::current_path()) {
+ parsing_context.push_front(parse_context_t(stream, cwd));
+ }
+ void push(const path& pathname,
+ const path& cwd = filesystem::current_path()) {
+ parsing_context.push_front(open_for_reading(pathname, cwd));
+ }
+
+ void push(const parse_context_t& context) {
+ parsing_context.push_front(context);
+ }
+
+ void pop() {
+ assert(! parsing_context.empty());
+ parsing_context.pop_front();
+ }
+
+ parse_context_t& get_current() {
+ assert(! parsing_context.empty());
+ return parsing_context.front();
+ }
+};
+
+} // namespace ledger
+
+#endif // _CONTEXT_H
diff --git a/src/convert.cc b/src/convert.cc
index 1b1bf814..e8ca241e 100644
--- a/src/convert.cc
+++ b/src/convert.cc
@@ -63,12 +63,16 @@ value_t convert_command(call_scope_t& args)
print_xacts formatter(report);
path csv_file_path(args.get<string>(0));
- ifstream data(csv_file_path);
- csv_reader reader(data, csv_file_path);
+
+ report.session.parsing_context.push(csv_file_path);
+ parse_context_t& context(report.session.parsing_context.get_current());
+ context.journal = &journal;
+ context.master = bucket;
+
+ csv_reader reader(context);
try {
- while (xact_t * xact = reader.read_xact(journal, bucket,
- report.HANDLED(rich_data))) {
+ while (xact_t * xact = reader.read_xact(report.HANDLED(rich_data))) {
if (report.HANDLED(invert)) {
foreach (post_t * post, xact->posts)
post->amount.in_place_negate();
diff --git a/src/csv.cc b/src/csv.cc
index 823238c7..305db992 100644
--- a/src/csv.cc
+++ b/src/csv.cc
@@ -40,27 +40,27 @@
namespace ledger {
-string csv_reader::read_field(std::istream& sin)
+string csv_reader::read_field(std::istream& in)
{
string field;
char c;
- if (sin.peek() == '"' || sin.peek() == '|') {
- sin.get(c);
+ if (in.peek() == '"' || in.peek() == '|') {
+ in.get(c);
char x;
- while (sin.good() && ! sin.eof()) {
- sin.get(x);
+ while (in.good() && ! in.eof()) {
+ in.get(x);
if (x == '\\') {
- sin.get(x);
+ in.get(x);
}
- else if (x == '"' && sin.peek() == '"') {
- sin.get(x);
+ else if (x == '"' && in.peek() == '"') {
+ in.get(x);
}
else if (x == c) {
if (x == '|')
- sin.unget();
- else if (sin.peek() == ',')
- sin.get(c);
+ in.unget();
+ else if (in.peek() == ',')
+ in.get(c);
break;
}
if (x != '\0')
@@ -68,9 +68,9 @@ string csv_reader::read_field(std::istream& sin)
}
}
else {
- while (sin.good() && ! sin.eof()) {
- sin.get(c);
- if (sin.good()) {
+ while (in.good() && ! in.eof()) {
+ in.get(c);
+ if (in.good()) {
if (c == ',')
break;
if (c != '\0')
@@ -82,22 +82,22 @@ string csv_reader::read_field(std::istream& sin)
return field;
}
-char * csv_reader::next_line(std::istream& sin)
+char * csv_reader::next_line(std::istream& in)
{
- while (sin.good() && ! sin.eof() && sin.peek() == '#')
- sin.getline(linebuf, MAX_LINE);
+ while (in.good() && ! in.eof() && in.peek() == '#')
+ in.getline(context.linebuf, parse_context_t::MAX_LINE);
- if (! sin.good() || sin.eof())
+ if (! in.good() || in.eof())
return NULL;
- sin.getline(linebuf, MAX_LINE);
+ in.getline(context.linebuf, parse_context_t::MAX_LINE);
- return linebuf;
+ return context.linebuf;
}
-void csv_reader::read_index(std::istream& sin)
+void csv_reader::read_index(std::istream& in)
{
- char * line = next_line(sin);
+ char * line = next_line(in);
if (! line)
return;
@@ -130,13 +130,12 @@ void csv_reader::read_index(std::istream& sin)
}
}
-xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket,
- bool rich_data)
+xact_t * csv_reader::read_xact(bool rich_data)
{
- char * line = next_line(in);
+ char * line = next_line(*context.stream.get());
if (! line || index.empty())
return NULL;
- linenum++;
+ context.linenum++;
std::istringstream instr(line);
@@ -146,18 +145,18 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket,
xact->set_state(item_t::CLEARED);
xact->pos = position_t();
- xact->pos->pathname = pathname;
- xact->pos->beg_pos = in.tellg();
- xact->pos->beg_line = linenum;
- xact->pos->sequence = sequence++;
+ xact->pos->pathname = context.pathname;
+ xact->pos->beg_pos = context.stream->tellg();
+ xact->pos->beg_line = context.linenum;
+ xact->pos->sequence = context.sequence++;
post->xact = xact.get();
post->pos = position_t();
- post->pos->pathname = pathname;
- post->pos->beg_pos = in.tellg();
- post->pos->beg_line = linenum;
- post->pos->sequence = sequence++;
+ post->pos->pathname = context.pathname;
+ post->pos->beg_pos = context.stream->tellg();
+ post->pos->beg_line = context.linenum;
+ post->pos->sequence = context.sequence++;
post->set_state(item_t::CLEARED);
post->account = NULL;
@@ -186,7 +185,7 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket,
case FIELD_PAYEE: {
bool found = false;
- foreach (payee_mapping_t& value, journal.payee_mappings) {
+ foreach (payee_mapping_t& value, context.journal->payee_mappings) {
DEBUG("csv.mappings", "Looking for payee mapping: " << value.first);
if (value.first.match(field)) {
xact->payee = value.second;
@@ -244,7 +243,7 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket,
// Translate the account name, if we have enough information to do so
- foreach (account_mapping_t& value, journal.account_mappings) {
+ foreach (account_mapping_t& value, context.journal->account_mappings) {
if (value.first.match(xact->payee)) {
post->account = value.second;
break;
@@ -260,13 +259,13 @@ xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket,
post->xact = xact.get();
post->pos = position_t();
- post->pos->pathname = pathname;
- post->pos->beg_pos = in.tellg();
- post->pos->beg_line = linenum;
- post->pos->sequence = sequence++;
+ post->pos->pathname = context.pathname;
+ post->pos->beg_pos = context.stream->tellg();
+ post->pos->beg_line = context.linenum;
+ post->pos->sequence = context.sequence++;
post->set_state(item_t::CLEARED);
- post->account = bucket;
+ post->account = context.master;
if (! amt.is_null())
post->amount = - amt;
diff --git a/src/csv.h b/src/csv.h
index 4d6e1253..24ea9121 100644
--- a/src/csv.h
+++ b/src/csv.h
@@ -43,6 +43,7 @@
#define _CSV_H
#include "value.h"
+#include "context.h"
namespace ledger {
@@ -52,13 +53,7 @@ class account_t;
class csv_reader
{
- static const std::size_t MAX_LINE = 4096;
-
- std::istream& in;
- path pathname;
- char linebuf[MAX_LINE];
- std::size_t linenum;
- std::size_t sequence;
+ parse_context_t context;
enum headers_t {
FIELD_DATE = 0,
@@ -86,9 +81,8 @@ class csv_reader
std::vector<string> names;
public:
- csv_reader(std::istream& _in, const path& _pathname)
- : in(_in), pathname(_pathname),
- linenum(0), sequence(0),
+ csv_reader(parse_context_t& _context)
+ : context(_context),
date_mask("date"),
date_aux_mask("posted( ?date)?"),
code_mask("code"),
@@ -97,32 +91,23 @@ public:
cost_mask("cost"),
total_mask("total"),
note_mask("note") {
- read_index(in);
+ read_index(*context.stream.get());
}
void read_index(std::istream& in);
string read_field(std::istream& in);
char * next_line(std::istream& in);
- xact_t * read_xact(journal_t& journal, account_t * bucket, bool rich_data);
+ xact_t * read_xact(bool rich_data);
const char * get_last_line() const {
- return linebuf;
+ return context.linebuf;
}
-
path get_pathname() const {
- return pathname;
+ return context.pathname;
}
std::size_t get_linenum() const {
- return linenum;
- }
-
- void reset() {
- pathname.clear();
- index.clear();
- names.clear();
- linenum = 0;
- sequence = 0;
+ return context.linenum;
}
};
diff --git a/src/generate.cc b/src/generate.cc
index bf9a8036..edd58632 100644
--- a/src/generate.cc
+++ b/src/generate.cc
@@ -360,9 +360,15 @@ void generate_posts_iterator::increment()
DEBUG("generate.post", "The post we intend to parse:\n" << buf.str());
- std::istringstream in(buf.str());
try {
- if (session.journal->parse(in, session) != 0) {
+ shared_ptr<std::istringstream> in(new std::istringstream(buf.str()));
+
+ parse_context_stack_t parsing_context;
+ parsing_context.push(in);
+ parsing_context.get_current().journal = session.journal.get();
+ parsing_context.get_current().scope = &session;
+
+ if (session.journal->read(parsing_context) != 0) {
VERIFY(session.journal->xacts.back()->valid());
posts.reset(*session.journal->xacts.back());
post = *posts++;
diff --git a/src/global.cc b/src/global.cc
index ee921fc5..5b7bb1c1 100644
--- a/src/global.cc
+++ b/src/global.cc
@@ -112,9 +112,12 @@ void global_scope_t::read_init()
if (exists(init_file)) {
TRACE_START(init, 1, "Read initialization file");
- ifstream init(init_file);
+ parse_context_stack_t parsing_context;
+ parsing_context.push(init_file);
+ parsing_context.get_current().journal = session().journal.get();
+ parsing_context.get_current().scope = &report();
- if (session().journal->read(init_file, NULL, &report()) > 0 ||
+ if (session().journal->read(parsing_context) > 0 ||
session().journal->auto_xacts.size() > 0 ||
session().journal->period_xacts.size() > 0) {
throw_(parse_error, _("Transactions found in initialization file '%1'")
diff --git a/src/journal.cc b/src/journal.cc
index 2ebe90fb..55c89dd9 100644
--- a/src/journal.cc
+++ b/src/journal.cc
@@ -32,6 +32,7 @@
#include <system.hh>
#include "journal.h"
+#include "context.h"
#include "amount.h"
#include "commodity.h"
#include "pool.h"
@@ -47,6 +48,7 @@ journal_t::journal_t()
initialize();
}
+#if 0
journal_t::journal_t(const path& pathname)
{
TRACE_CTOR(journal_t, "path");
@@ -60,6 +62,7 @@ journal_t::journal_t(const string& str)
initialize();
read(str);
}
+#endif
journal_t::~journal_t()
{
@@ -87,6 +90,7 @@ void journal_t::initialize()
fixed_payees = false;
fixed_commodities = false;
fixed_metadata = false;
+ current_context = NULL;
was_loaded = false;
force_checking = false;
check_payees = false;
@@ -114,7 +118,6 @@ account_t * journal_t::find_account_re(const string& regexp)
}
account_t * journal_t::register_account(const string& name, post_t * post,
- const string& location,
account_t * master_account)
{
account_t * result = NULL;
@@ -147,8 +150,8 @@ account_t * journal_t::register_account(const string& name, post_t * post,
result->add_flags(ACCOUNT_KNOWN);
}
else if (checking_style == CHECK_WARNING) {
- warning_(_("%1Unknown account '%2'") << location
- << result->fullname());
+ current_context->warning(STR(_("Unknown account '%1'")
+ << result->fullname()));
}
else if (checking_style == CHECK_ERROR) {
throw_(parse_error, _("Unknown account '%1'") << result->fullname());
@@ -159,8 +162,7 @@ account_t * journal_t::register_account(const string& name, post_t * post,
return result;
}
-string journal_t::register_payee(const string& name, xact_t * xact,
- const string& location)
+string journal_t::register_payee(const string& name, xact_t * xact)
{
string payee;
@@ -178,7 +180,7 @@ string journal_t::register_payee(const string& name, xact_t * xact,
known_payees.insert(name);
}
else if (checking_style == CHECK_WARNING) {
- warning_(_("%1Unknown payee '%2'") << location << name);
+ current_context->warning(STR(_("Unknown payee '%1'") << name));
}
else if (checking_style == CHECK_ERROR) {
throw_(parse_error, _("Unknown payee '%1'") << name);
@@ -197,8 +199,7 @@ string journal_t::register_payee(const string& name, xact_t * xact,
}
void journal_t::register_commodity(commodity_t& comm,
- variant<int, xact_t *, post_t *> context,
- const string& location)
+ variant<int, xact_t *, post_t *> context)
{
if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) {
if (! comm.has_flags(COMMODITY_KNOWN)) {
@@ -215,7 +216,7 @@ void journal_t::register_commodity(commodity_t& comm,
comm.add_flags(COMMODITY_KNOWN);
}
else if (checking_style == CHECK_WARNING) {
- warning_(_("%1Unknown commodity '%2'") << location << comm);
+ current_context->warning(STR(_("Unknown commodity '%1'") << comm));
}
else if (checking_style == CHECK_ERROR) {
throw_(parse_error, _("Unknown commodity '%1'") << comm);
@@ -224,40 +225,8 @@ void journal_t::register_commodity(commodity_t& comm,
}
}
-namespace {
- void check_metadata(journal_t& journal,
- const string& key, const value_t& value,
- variant<int, xact_t *, post_t *> context,
- const string& location)
- {
- std::pair<tag_check_exprs_map::iterator,
- tag_check_exprs_map::iterator> range =
- journal.tag_check_exprs.equal_range(key);
-
- for (tag_check_exprs_map::iterator i = range.first;
- i != range.second;
- ++i) {
- value_scope_t val_scope
- (context.which() == 1 ?
- static_cast<scope_t&>(*boost::get<xact_t *>(context)) :
- static_cast<scope_t&>(*boost::get<post_t *>(context)), value);
-
- if (! (*i).second.first.calc(val_scope).to_boolean()) {
- if ((*i).second.second == expr_t::EXPR_ASSERTION)
- throw_(parse_error,
- _("Metadata assertion failed for (%1: %2): %3")
- << key << value << (*i).second.first);
- else
- warning_(_("%1Metadata check failed for (%2: %3): %4")
- << location << key << value << (*i).second.first);
- }
- }
- }
-}
-
void journal_t::register_metadata(const string& key, const value_t& value,
- variant<int, xact_t *, post_t *> context,
- const string& location)
+ variant<int, xact_t *, post_t *> context)
{
if (checking_style == CHECK_WARNING || checking_style == CHECK_ERROR) {
std::set<string>::iterator i = known_tags.find(key);
@@ -276,7 +245,7 @@ void journal_t::register_metadata(const string& key, const value_t& value,
known_tags.insert(key);
}
else if (checking_style == CHECK_WARNING) {
- warning_(_("%1Unknown metadata tag '%2'") << location << key);
+ current_context->warning(STR(_("Unknown metadata tag '%1'") << key));
}
else if (checking_style == CHECK_ERROR) {
throw_(parse_error, _("Unknown metadata tag '%1'") << key);
@@ -284,8 +253,33 @@ void journal_t::register_metadata(const string& key, const value_t& value,
}
}
- if (! value.is_null())
- check_metadata(*this, key, value, context, location);
+ if (! value.is_null()) {
+ std::pair<tag_check_exprs_map::iterator,
+ tag_check_exprs_map::iterator> range =
+ tag_check_exprs.equal_range(key);
+
+ for (tag_check_exprs_map::iterator i = range.first;
+ i != range.second;
+ ++i) {
+ bind_scope_t bound_scope
+ (*current_context->scope,
+ context.which() == 1 ?
+ static_cast<scope_t&>(*boost::get<xact_t *>(context)) :
+ static_cast<scope_t&>(*boost::get<post_t *>(context)));
+ value_scope_t val_scope(bound_scope, value);
+
+ if (! (*i).second.first.calc(val_scope).to_boolean()) {
+ if ((*i).second.second == expr_t::EXPR_ASSERTION)
+ throw_(parse_error,
+ _("Metadata assertion failed for (%1: %2): %3")
+ << key << value << (*i).second.first);
+ else
+ current_context->warning
+ (STR(_("Metadata check failed for (%1: %2): %3")
+ << key << value << (*i).second.first));
+ }
+ }
+ }
}
namespace {
@@ -300,13 +294,10 @@ namespace {
xact ? *xact->metadata : *post->metadata) {
const string& key(pair.first);
- // jww (2012-02-27): We really need to know the parsing context,
- // both here and for the call to warning_ in
- // xact_t::extend_xact.
if (optional<value_t> value = pair.second.first)
- journal.register_metadata(key, *value, context, "");
+ journal.register_metadata(key, *value, context);
else
- journal.register_metadata(key, NULL_VALUE, context, "");
+ journal.register_metadata(key, NULL_VALUE, context);
}
}
}
@@ -351,7 +342,7 @@ bool journal_t::add_xact(xact_t * xact)
void journal_t::extend_xact(xact_base_t * xact)
{
foreach (auto_xact_t * auto_xact, auto_xacts)
- auto_xact->extend_xact(*xact);
+ auto_xact->extend_xact(*xact, *current_context);
}
bool journal_t::remove_xact(xact_t * xact)
@@ -372,25 +363,36 @@ bool journal_t::remove_xact(xact_t * xact)
return true;
}
-std::size_t journal_t::read(std::istream& in,
- const path& pathname,
- account_t * master_alt,
- scope_t * scope)
+std::size_t journal_t::read(parse_context_stack_t& context)
{
std::size_t count = 0;
try {
- if (! scope)
- scope = scope_t::default_scope;
+ parse_context_t& current(context.get_current());
+ current_context = &current;
+
+ current.count = 0;
+ if (! current.scope)
+ current.scope = scope_t::default_scope;
- if (! scope)
+ if (! current.scope)
throw_(std::runtime_error,
_("No default scope in which to read journal file '%1'")
- << pathname);
+ << current.pathname);
- count = parse(in, *scope, master_alt ? master_alt : master, &pathname);
+ if (! current.master)
+ current.master = master;
+
+ count = read_textual(context);
+ if (count > 0) {
+ if (! current.pathname.empty())
+ sources.push_back(fileinfo_t(current.pathname));
+ else
+ sources.push_back(fileinfo_t());
+ }
}
catch (...) {
clear_xdata();
+ current_context = NULL;
throw;
}
@@ -402,23 +404,6 @@ std::size_t journal_t::read(std::istream& in,
return count;
}
-std::size_t journal_t::read(const path& pathname,
- account_t * master_account,
- scope_t * scope)
-{
- path filename = resolve_path(pathname);
-
- if (! exists(filename))
- throw_(std::runtime_error,
- _("Cannot read journal file %1") << filename);
-
- ifstream stream(filename);
- std::size_t count = read(stream, filename, master_account, scope);
- if (count > 0)
- sources.push_back(fileinfo_t(filename));
- return count;
-}
-
bool journal_t::has_xdata()
{
foreach (xact_t * xact, xacts)
diff --git a/src/journal.h b/src/journal.h
index 6d10ffda..8b750993 100644
--- a/src/journal.h
+++ b/src/journal.h
@@ -55,7 +55,8 @@ class auto_xact_t;
class period_xact_t;
class post_t;
class account_t;
-class scope_t;
+class parse_context_t;
+class parse_context_stack_t;
typedef std::list<xact_t *> xacts_list;
typedef std::list<auto_xact_t *> auto_xacts_list;
@@ -132,6 +133,7 @@ public:
account_mappings_t payees_for_unknown_accounts;
checksum_map_t checksum_map;
tag_check_exprs_map tag_check_exprs;
+ parse_context_t * current_context;
bool was_loaded;
bool force_checking;
bool check_payees;
@@ -143,8 +145,10 @@ public:
} checking_style;
journal_t();
+#if 0
journal_t(const path& pathname);
journal_t(const string& str);
+#endif
~journal_t();
void initialize();
@@ -162,16 +166,12 @@ public:
account_t * find_account_re(const string& regexp);
account_t * register_account(const string& name, post_t * post,
- const string& location,
account_t * master = NULL);
- string register_payee(const string& name, xact_t * xact,
- const string& location);
+ string register_payee(const string& name, xact_t * xact);
void register_commodity(commodity_t& comm,
- variant<int, xact_t *, post_t *> context,
- const string& location);
+ variant<int, xact_t *, post_t *> context);
void register_metadata(const string& key, const value_t& value,
- variant<int, xact_t *, post_t *> context,
- const string& location);
+ variant<int, xact_t *, post_t *> context);
bool add_xact(xact_t * xact);
void extend_xact(xact_base_t * xact);
@@ -196,24 +196,16 @@ public:
return period_xacts.end();
}
- std::size_t read(std::istream& in,
- const path& pathname,
- account_t * master = NULL,
- scope_t * scope = NULL);
- std::size_t read(const path& pathname,
- account_t * master = NULL,
- scope_t * scope = NULL);
-
- std::size_t parse(std::istream& in,
- scope_t& session_scope,
- account_t * master = NULL,
- const path * original_file = NULL);
+ std::size_t read(parse_context_stack_t& context);
bool has_xdata();
void clear_xdata();
bool valid() const;
+private:
+ std::size_t read_textual(parse_context_stack_t& context);
+
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
diff --git a/src/precmd.cc b/src/precmd.cc
index 6f8becb6..6b106a8b 100644
--- a/src/precmd.cc
+++ b/src/precmd.cc
@@ -83,8 +83,14 @@ namespace {
out << _("--- Context is first posting of the following transaction ---")
<< std::endl << str << std::endl;
{
- std::istringstream in(str);
- report.session.journal->parse(in, report.session);
+ shared_ptr<std::istringstream> in(new std::istringstream(str));
+
+ parse_context_stack_t parsing_context;
+ parsing_context.push(in);
+ parsing_context.get_current().journal = report.session.journal.get();
+ parsing_context.get_current().scope = &report.session;
+
+ report.session.journal->read(parsing_context);
report.session.journal->clear_xdata();
}
}
diff --git a/src/py_commodity.cc b/src/py_commodity.cc
index 11ebe844..ffa903f4 100644
--- a/src/py_commodity.cc
+++ b/src/py_commodity.cc
@@ -113,7 +113,7 @@ namespace {
if (i == pool.commodities.end()) {
PyErr_SetString(PyExc_ValueError,
(string("Could not find commodity ") + symbol).c_str());
- throw boost::python::error_already_set();
+ throw_error_already_set();
}
return (*i).second;
}
diff --git a/src/py_journal.cc b/src/py_journal.cc
index 4f5427f5..a72b8528 100644
--- a/src/py_journal.cc
+++ b/src/py_journal.cc
@@ -135,10 +135,12 @@ namespace {
return journal.find_account(name, auto_create);
}
+#if 0
std::size_t py_read(journal_t& journal, const string& pathname)
{
- return journal.read(pathname);
+ return journal.read(context_stack);
}
+#endif
struct collector_wrapper
{
@@ -264,9 +266,10 @@ void export_journal()
;
class_< journal_t, boost::noncopyable > ("Journal")
+#if 0
.def(init<path>())
.def(init<string>())
-
+#endif
.add_property("master",
make_getter(&journal_t::master,
return_internal_reference<1,
@@ -311,9 +314,9 @@ void export_journal()
(&journal_t::period_xacts_begin, &journal_t::period_xacts_end))
.def("sources", python::range<return_internal_reference<> >
(&journal_t::sources_begin, &journal_t::sources_end))
-
+#if 0
.def("read", py_read)
-
+#endif
.def("has_xdata", &journal_t::has_xdata)
.def("clear_xdata", &journal_t::clear_xdata)
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index 44bea2cd..de9c94cb 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -194,18 +194,19 @@ object python_interpreter_t::import_option(const string& str)
if (! is_initialized)
initialize();
- path file(str);
- string name(str);
-
python::object sys_module = python::import("sys");
python::object sys_dict = sys_module.attr("__dict__");
+ path file(str);
+ string name(str);
python::list paths(sys_dict["path"]);
if (contains(str, ".py")) {
#if BOOST_VERSION >= 103700
- path& cwd(get_parsing_context().current_directory);
- paths.insert(0, filesystem::absolute(file, cwd).parent_path().string());
+ path& cwd(parsing_context.get_current().current_directory);
+ path parent(filesystem::absolute(file, cwd).parent_path());
+ DEBUG("python.interp", "Adding " << parent << " to PYTHONPATH");
+ paths.insert(0, parent.string());
sys_dict["path"] = paths;
#if BOOST_VERSION >= 104600
@@ -220,7 +221,24 @@ object python_interpreter_t::import_option(const string& str)
#endif // BOOST_VERSION >= 103700
}
- return python::import(python::str(name.c_str()));
+ try {
+ if (contains(str, ".py")) {
+ import_into_main(name);
+ } else {
+ object obj = python::import(python::str(name.c_str()));
+ main_nspace[name.c_str()] = obj;
+ return obj;
+ }
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::runtime_error, _("Python failed to import: %1") << str);
+ }
+ catch (...) {
+ throw;
+ }
+
+ return object();
}
object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
@@ -348,13 +366,13 @@ value_t python_interpreter_t::server_command(call_scope_t& args)
functor_t func(main_function, "main");
try {
func(args);
+ return true;
}
catch (const error_already_set&) {
PyErr_Print();
throw_(std::runtime_error,
_("Error while invoking ledger.server's main() function"));
}
- return true;
} else {
throw_(std::runtime_error,
_("The ledger.server module is missing its main() function!"));
@@ -455,6 +473,7 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
if (! PyCallable_Check(func.ptr())) {
extract<value_t> val(func);
+ DEBUG("python.interp", "Value of Python '" << name << "': " << val);
std::signal(SIGINT, sigint_handler);
if (val.check())
return val();
@@ -476,6 +495,8 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
value_t result;
if (xval.check()) {
result = xval();
+ DEBUG("python.interp",
+ "Return from Python '" << name << "': " << result);
Py_DECREF(val);
} else {
Py_DECREF(val);
diff --git a/src/scope.h b/src/scope.h
index 6fcd67e9..c42ec66b 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -664,7 +664,7 @@ public:
if (name == "value")
return MAKE_FUNCTOR(value_scope_t::get_value);
- return NULL;
+ return child_scope_t::lookup(kind, name);
}
};
diff --git a/src/session.cc b/src/session.cc
index 9d994a9b..e07026b6 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -120,8 +120,17 @@ std::size_t session_t::read_data(const string& master_account)
#endif // HAVE_BOOST_SERIALIZATION
if (price_db_path) {
if (exists(*price_db_path)) {
- if (journal->read(*price_db_path) > 0)
- throw_(parse_error, _("Transactions not allowed in price history file"));
+ parsing_context.push(*price_db_path);
+ parsing_context.get_current().journal = journal.get();
+ try {
+ if (journal->read(parsing_context) > 0)
+ throw_(parse_error, _("Transactions not allowed in price history file"));
+ }
+ catch (...) {
+ parsing_context.pop();
+ throw;
+ }
+ parsing_context.pop();
}
}
@@ -140,12 +149,22 @@ std::size_t session_t::read_data(const string& master_account)
}
buffer.flush();
- std::istringstream buf_in(buffer.str());
- xact_count += journal->read(buf_in, "/dev/stdin", acct);
- journal->sources.push_back(journal_t::fileinfo_t());
+ shared_ptr<std::istream> stream(new std::istringstream(buffer.str()));
+ parsing_context.push(stream);
} else {
- xact_count += journal->read(pathname, acct);
+ parsing_context.push(pathname);
+ }
+
+ parsing_context.get_current().journal = journal.get();
+ parsing_context.get_current().master = acct;
+ try {
+ xact_count += journal->read(parsing_context);
+ }
+ catch (...) {
+ parsing_context.pop();
+ throw;
}
+ parsing_context.pop();
}
DEBUG("ledger.read", "xact_count [" << xact_count
diff --git a/src/session.h b/src/session.h
index 93bee8ba..54b9912a 100644
--- a/src/session.h
+++ b/src/session.h
@@ -44,6 +44,7 @@
#include "account.h"
#include "journal.h"
+#include "context.h"
#include "option.h"
#include "commodity.h"
@@ -57,7 +58,9 @@ class session_t : public symbol_scope_t
public:
bool flush_on_next_data_file;
+
std::auto_ptr<journal_t> journal;
+ parse_context_stack_t parsing_context;
explicit session_t();
virtual ~session_t() {
diff --git a/src/textual.cc b/src/textual.cc
index 15642cae..97c80e4f 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -32,6 +32,7 @@
#include <system.hh>
#include "journal.h"
+#include "context.h"
#include "xact.h"
#include "post.h"
#include "account.h"
@@ -53,8 +54,10 @@ namespace {
struct application_t
{
string label;
- variant<account_t *, string, fixed_rate_t> value;
+ variant<optional<datetime_t>, account_t *, string, fixed_rate_t> value;
+ application_t(string _label, optional<datetime_t> epoch)
+ : label(_label), value(epoch) {}
application_t(string _label, account_t * acct)
: label(_label), value(acct) {}
application_t(string _label, string tag)
@@ -63,24 +66,27 @@ namespace {
: label(_label), value(rate) {}
};
- class parse_context_t : public noncopyable
+ class instance_t : public noncopyable, public scope_t
{
+
public:
- std::list<application_t> apply_stack;
+ parse_context_stack_t& context_stack;
+ parse_context_t& context;
+ std::istream& in;
+ instance_t * parent;
- journal_t& journal;
- scope_t& scope;
+ std::list<application_t> apply_stack;
#if defined(TIMELOG_SUPPORT)
- time_log_t timelog;
+ time_log_t timelog;
#endif
- std::size_t count;
- std::size_t errors;
- std::size_t sequence;
-
- parse_context_t(journal_t& _journal, scope_t& _scope)
- : journal(_journal), scope(_scope), timelog(journal, scope),
- count(0), errors(0), sequence(1) {
- timelog.context_count = &count;
+
+ instance_t(parse_context_stack_t& _context_stack,
+ parse_context_t& _context, instance_t * _parent = NULL)
+ : context_stack(_context_stack), context(_context),
+ in(*context.stream.get()), parent(_parent), timelog(context) {}
+
+ virtual string description() {
+ return _("textual parser");
}
account_t * top_account() {
@@ -90,40 +96,10 @@ namespace {
return NULL;
}
- void close() {
- timelog.close();
- }
- };
-
- class instance_t : public noncopyable, public scope_t
- {
- static const std::size_t MAX_LINE = 1024;
-
- public:
- parse_context_t& context;
- instance_t * parent;
- const path * original_file;
- path pathname;
- std::istream& in;
- char linebuf[MAX_LINE + 1];
- std::size_t linenum;
- istream_pos_type line_beg_pos;
- istream_pos_type curr_pos;
- optional<datetime_t> prev_epoch;
-
- instance_t(parse_context_t& _context,
- std::istream& _in,
- const path * _original_file = NULL,
- instance_t * _parent = NULL);
-
- ~instance_t();
-
- virtual string description() {
- return _("textual parser");
- }
-
void parse();
+
std::streamsize read_line(char *& line);
+
bool peek_whitespace_line() {
return (in.good() && ! in.eof() &&
(in.peek() == ' ' || in.peek() == '\t'));
@@ -229,37 +205,17 @@ namespace {
}
}
-instance_t::instance_t(parse_context_t& _context,
- std::istream& _in,
- const path * _original_file,
- instance_t * _parent)
- : context(_context), parent(_parent), original_file(_original_file),
- pathname(original_file ? *original_file : "/dev/stdin"), in(_in)
-{
- TRACE_CTOR(instance_t, "...");
- DEBUG("times.epoch", "Saving epoch " << epoch);
- prev_epoch = epoch; // declared in times.h
-}
-
-instance_t::~instance_t()
-{
- TRACE_DTOR(instance_t);
- epoch = prev_epoch;
- DEBUG("times.epoch", "Restored epoch to " << epoch);
-}
-
void instance_t::parse()
{
- INFO("Parsing file '" << pathname.string() << "'");
+ INFO("Parsing file " << context.pathname);
- TRACE_START(instance_parse, 1,
- "Done parsing file '" << pathname.string() << "'");
+ TRACE_START(instance_parse, 1, "Done parsing file " << context.pathname);
if (! in.good() || in.eof())
return;
- linenum = 0;
- curr_pos = in.tellg();
+ context.linenum = 0;
+ context.curr_pos = in.tellg();
while (in.good() && ! in.eof()) {
try {
@@ -278,11 +234,9 @@ void instance_t::parse()
foreach (instance_t * instance, instances)
add_error_context(_("In file included from %1")
- << file_context(instance->pathname,
- instance->linenum));
+ << instance->context.location());
}
- add_error_context(_("While parsing file %1")
- << file_context(pathname, linenum));
+ add_error_context(_("While parsing file %1") << context.location());
if (caught_signal != NONE_CAUGHT)
throw;
@@ -307,26 +261,26 @@ 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;
+ context.line_beg_pos = context.curr_pos;
check_for_signal();
- in.getline(linebuf, MAX_LINE);
+ in.getline(context.linebuf, parse_context_t::MAX_LINE);
std::streamsize len = in.gcount();
if (len > 0) {
- if (linenum == 0 && utf8::is_bom(linebuf))
- line = &linebuf[3];
+ if (context.linenum == 0 && utf8::is_bom(context.linebuf))
+ line = &context.linebuf[3];
else
- line = linebuf;
+ line = context.linebuf;
if (line[len - 1] == '\r') // strip Windows CRLF down to LF
line[--len] = '\0';
- linenum++;
+ context.linenum++;
- curr_pos = line_beg_pos;
- curr_pos += len;
+ context.curr_pos = context.line_beg_pos;
+ context.curr_pos += len;
return len - 1; // LF is being silently dropped
}
@@ -446,19 +400,19 @@ void instance_t::clock_in_directive(char * line, bool /*capitalized*/)
end = NULL;
position_t position;
- position.pathname = pathname;
- position.beg_pos = line_beg_pos;
- position.beg_line = linenum;
- position.end_pos = curr_pos;
- position.end_line = linenum;
+ position.pathname = context.pathname;
+ position.beg_pos = context.line_beg_pos;
+ position.beg_line = context.linenum;
+ position.end_pos = context.curr_pos;
+ position.end_line = context.linenum;
position.sequence = context.sequence++;
time_xact_t event(position, parse_datetime(datetime),
- p ? context.top_account()->find_account(p) : NULL,
+ p ? top_account()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
- context.timelog.clock_in(event);
+ timelog.clock_in(event);
}
void instance_t::clock_out_directive(char * line, bool /*capitalized*/)
@@ -475,19 +429,19 @@ void instance_t::clock_out_directive(char * line, bool /*capitalized*/)
end = NULL;
position_t position;
- position.pathname = pathname;
- position.beg_pos = line_beg_pos;
- position.beg_line = linenum;
- position.end_pos = curr_pos;
- position.end_line = linenum;
+ position.pathname = context.pathname;
+ position.beg_pos = context.line_beg_pos;
+ position.beg_line = context.linenum;
+ position.end_pos = context.curr_pos;
+ position.end_line = context.linenum;
position.sequence = context.sequence++;
time_xact_t event(position, parse_datetime(datetime),
- p ? context.top_account()->find_account(p) : NULL,
+ p ? top_account()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
- context.timelog.clock_out(event);
+ timelog.clock_out(event);
context.count++;
}
@@ -503,8 +457,8 @@ void instance_t::default_commodity_directive(char * line)
void instance_t::default_account_directive(char * line)
{
- context.journal.bucket = context.top_account()->find_account(skip_ws(line + 1));
- context.journal.bucket->add_flags(ACCOUNT_KNOWN);
+ context.journal->bucket = top_account()->find_account(skip_ws(line + 1));
+ context.journal->bucket->add_flags(ACCOUNT_KNOWN);
}
void instance_t::price_conversion_directive(char * line)
@@ -543,13 +497,14 @@ void instance_t::option_directive(char * line)
*p++ = '\0';
}
- if (! process_option(pathname.string(), line + 2, context.scope, p, line))
+ if (! process_option(context.pathname.string(), line + 2,
+ *context.scope, p, line))
throw_(option_error, _("Illegal option --%1") << line + 2);
}
void instance_t::automated_xact_directive(char * line)
{
- istream_pos_type pos= line_beg_pos;
+ istream_pos_type pos = context.line_beg_pos;
bool reveal_context = true;
@@ -562,9 +517,9 @@ void instance_t::automated_xact_directive(char * line)
std::auto_ptr<auto_xact_t> ae(new auto_xact_t(predicate_t(expr, keeper)));
ae->pos = position_t();
- ae->pos->pathname = pathname;
- ae->pos->beg_pos = line_beg_pos;
- ae->pos->beg_line = linenum;
+ ae->pos->pathname = context.pathname;
+ ae->pos->beg_pos = context.line_beg_pos;
+ ae->pos->beg_line = context.linenum;
ae->pos->sequence = context.sequence++;
post_t * last_post = NULL;
@@ -586,9 +541,9 @@ void instance_t::automated_xact_directive(char * line)
item = ae.get();
// This is a trailing note, and possibly a metadata info tag
- item->append_note(p + 1, context.scope, true);
+ item->append_note(p + 1, *context.scope, true);
item->add_flags(ITEM_NOTE_ON_NEXT_LINE);
- item->pos->end_pos = curr_pos;
+ item->pos->end_pos = context.curr_pos;
item->pos->end_line++;
// If there was no last_post yet, then deferred notes get applied to
@@ -619,8 +574,7 @@ void instance_t::automated_xact_directive(char * line)
reveal_context = false;
if (post_t * post =
- parse_post(p, len - (p - line), context.top_account(),
- NULL, true)) {
+ parse_post(p, len - (p - line), top_account(), NULL, true)) {
reveal_context = true;
ae->add_post(post);
last_post = post;
@@ -629,18 +583,19 @@ void instance_t::automated_xact_directive(char * line)
}
}
- context.journal.auto_xacts.push_back(ae.get());
+ context.journal->auto_xacts.push_back(ae.get());
- ae->journal = &context.journal;
- ae->pos->end_pos = curr_pos;
- ae->pos->end_line = linenum;
+ ae->journal = context.journal;
+ ae->pos->end_pos = context.curr_pos;
+ ae->pos->end_line = context.linenum;
ae.release();
}
catch (const std::exception&) {
if (reveal_context) {
add_error_context(_("While parsing automated transaction:"));
- add_error_context(source_context(pathname, pos, curr_pos, "> "));
+ add_error_context(source_context(context.pathname, pos,
+ context.curr_pos, "> "));
}
throw;
}
@@ -648,7 +603,7 @@ void instance_t::automated_xact_directive(char * line)
void instance_t::period_xact_directive(char * line)
{
- istream_pos_type pos = line_beg_pos;
+ istream_pos_type pos = context.line_beg_pos;
bool reveal_context = true;
@@ -656,23 +611,23 @@ void instance_t::period_xact_directive(char * line)
std::auto_ptr<period_xact_t> pe(new period_xact_t(skip_ws(line + 1)));
pe->pos = position_t();
- pe->pos->pathname = pathname;
- pe->pos->beg_pos = line_beg_pos;
- pe->pos->beg_line = linenum;
+ pe->pos->pathname = context.pathname;
+ pe->pos->beg_pos = context.line_beg_pos;
+ pe->pos->beg_line = context.linenum;
pe->pos->sequence = context.sequence++;
reveal_context = false;
- if (parse_posts(context.top_account(), *pe.get())) {
+ if (parse_posts(top_account(), *pe.get())) {
reveal_context = true;
- pe->journal = &context.journal;
+ pe->journal = context.journal;
if (pe->finalize()) {
- context.journal.extend_xact(pe.get());
- context.journal.period_xacts.push_back(pe.get());
+ context.journal->extend_xact(pe.get());
+ context.journal->period_xacts.push_back(pe.get());
- pe->pos->end_pos = curr_pos;
- pe->pos->end_line = linenum;
+ pe->pos->end_pos = context.curr_pos;
+ pe->pos->end_line = context.linenum;
pe.release();
} else {
@@ -686,7 +641,8 @@ void instance_t::period_xact_directive(char * line)
catch (const std::exception&) {
if (reveal_context) {
add_error_context(_("While parsing periodic transaction:"));
- add_error_context(source_context(pathname, pos, curr_pos, "> "));
+ add_error_context(source_context(context.pathname, pos,
+ context.curr_pos, "> "));
}
throw;
}
@@ -696,10 +652,10 @@ void instance_t::xact_directive(char * line, std::streamsize len)
{
TRACE_START(xacts, 1, "Time spent handling transactions:");
- if (xact_t * xact = parse_xact(line, len, context.top_account())) {
+ if (xact_t * xact = parse_xact(line, len, top_account())) {
std::auto_ptr<xact_t> manager(xact);
- if (context.journal.add_xact(xact)) {
+ if (context.journal->add_xact(xact)) {
manager.release(); // it's owned by the journal now
context.count++;
}
@@ -721,12 +677,13 @@ void instance_t::include_directive(char * line)
if (line[0] != '/' && line[0] != '\\' && line[0] != '~') {
DEBUG("textual.include", "received a relative path");
- DEBUG("textual.include", "parent file path: " << pathname.string());
- string::size_type pos = pathname.string().rfind('/');
+ DEBUG("textual.include", "parent file path: " << context.pathname);
+ string pathstr(context.pathname.string());
+ string::size_type pos = pathstr.rfind('/');
if (pos == string::npos)
- pos = pathname.string().rfind('\\');
+ pos = pathstr.rfind('\\');
if (pos != string::npos) {
- filename = path(string(pathname.string(), 0, pos + 1)) / line;
+ filename = path(string(pathstr, 0, pos + 1)) / line;
DEBUG("textual.include", "normalized path: " << filename.string());
} else {
filename = path(string(".")) / line;
@@ -773,10 +730,24 @@ void instance_t::include_directive(char * line)
string base = (*iter).leaf();
#endif // BOOST_VERSION >= 103700
if (glob.match(base)) {
- path inner_file(*iter);
- ifstream stream(inner_file);
- instance_t instance(context, stream, &inner_file, this);
- instance.parse();
+ journal_t * journal = context.journal;
+ account_t * master = context.master;
+
+ context_stack.push(*iter);
+
+ context_stack.get_current().journal = journal;
+ context_stack.get_current().master = master;
+ try {
+ instance_t instance(context_stack,
+ context_stack.get_current(), this);
+ instance.parse();
+ }
+ catch (...) {
+ context_stack.pop();
+ throw;
+ }
+ context_stack.pop();
+
files_found = true;
}
}
@@ -805,8 +776,8 @@ void instance_t::apply_directive(char * line)
void instance_t::apply_account_directive(char * line)
{
- if (account_t * acct = context.top_account()->find_account(line))
- context.apply_stack.push_front(application_t("account", acct));
+ if (account_t * acct = top_account()->find_account(line))
+ apply_stack.push_front(application_t("account", acct));
#if !defined(NO_ASSERTS)
else
assert("Failed to create account" == NULL);
@@ -820,14 +791,14 @@ void instance_t::apply_tag_directive(char * line)
if (tag.find(':') == string::npos)
tag = string(":") + tag + ":";
- context.apply_stack.push_front(application_t("tag", tag));
+ apply_stack.push_front(application_t("tag", tag));
}
void instance_t::apply_rate_directive(char * line)
{
if (optional<std::pair<commodity_t *, price_point_t> > price_point =
commodity_pool_t::current_pool->parse_price_directive(trim_ws(line), true)) {
- context.apply_stack.push_front
+ apply_stack.push_front
(application_t("fixed", fixed_rate_t(price_point->first,
price_point->second.price)));
} else {
@@ -837,11 +808,13 @@ void instance_t::apply_rate_directive(char * line)
void instance_t::apply_year_directive(char * line)
{
- unsigned short year(lexical_cast<unsigned short>(skip_ws(line + 1)));
- DEBUG("times.epoch", "Setting current year to " << year);
+ apply_stack.push_front(application_t("year", epoch));
+
// This must be set to the last day of the year, otherwise partial
// dates like "11/01" will refer to last year's november, not the
// current year.
+ unsigned short year(lexical_cast<unsigned short>(skip_ws(line + 1)));
+ DEBUG("times.epoch", "Setting current year to " << year);
epoch = datetime_t(date_t(year, 12, 31));
}
@@ -850,28 +823,30 @@ void instance_t::end_apply_directive(char * kind)
char * b = next_element(kind);
string name(b ? b : " ");
- if (context.apply_stack.size() <= 1)
+ if (apply_stack.size() <= 1)
throw_(std::runtime_error,
_("'end apply %1' found, but no enclosing 'apply %2' directive")
<< name << name);
- if (name != " " && name != context.apply_stack.front().label)
+ if (name != " " && name != apply_stack.front().label)
throw_(std::runtime_error,
_("'end apply %1' directive does not match 'apply %2' directive")
- << name << context.apply_stack.front().label);
+ << name << apply_stack.front().label);
+
+ if (apply_stack.front().value.type() == typeid(optional<datetime_t>))
+ epoch = boost::get<optional<datetime_t> >(apply_stack.front().value);
- context.apply_stack.pop_front();
+ apply_stack.pop_front();
}
void instance_t::account_directive(char * line)
{
- istream_pos_type beg_pos = line_beg_pos;
- std::size_t beg_linenum = linenum;
+ istream_pos_type beg_pos = context.line_beg_pos;
+ std::size_t beg_linenum = context.linenum;
char * p = skip_ws(line);
account_t * account =
- context.journal.register_account(p, NULL, file_context(pathname, linenum),
- context.top_account());
+ context.journal->register_account(p, NULL, top_account());
std::auto_ptr<auto_xact_t> ae;
while (peek_whitespace_line()) {
@@ -900,7 +875,7 @@ void instance_t::account_directive(char * line)
ae.reset(new auto_xact_t(pred));
ae->pos = position_t();
- ae->pos->pathname = pathname;
+ ae->pos->pathname = context.pathname;
ae->pos->beg_pos = beg_pos;
ae->pos->beg_line = beg_linenum;
ae->pos->sequence = context.sequence++;
@@ -916,7 +891,7 @@ void instance_t::account_directive(char * line)
else if (keyword == "eval" || keyword == "expr") {
// jww (2012-02-27): Make account into symbol scopes so that this
// can be used to override definitions within the account.
- bind_scope_t bound_scope(context.scope, *account);
+ bind_scope_t bound_scope(*context.scope, *account);
expr_t(b).calc(bound_scope);
}
else if (keyword == "note") {
@@ -925,11 +900,11 @@ void instance_t::account_directive(char * line)
}
if (ae.get()) {
- context.journal.auto_xacts.push_back(ae.get());
+ context.journal->auto_xacts.push_back(ae.get());
- ae->journal = &context.journal;
+ ae->journal = context.journal;
ae->pos->end_pos = in.tellg();
- ae->pos->end_line = linenum;
+ ae->pos->end_line = context.linenum;
ae.release();
}
@@ -943,7 +918,7 @@ void instance_t::account_alias_directive(account_t * account, string alias)
trim(alias);
std::pair<accounts_map::iterator, bool> result
= context.journal
- .account_aliases.insert(accounts_map::value_type(alias, account));
+ ->account_aliases.insert(accounts_map::value_type(alias, account));
assert(result.second);
}
@@ -957,26 +932,25 @@ void instance_t::alias_directive(char * line)
*e++ = '\0';
e = skip_ws(e);
- account_alias_directive(context.top_account()->find_account(e), b);
+ account_alias_directive(top_account()->find_account(e), b);
}
}
void instance_t::account_payee_directive(account_t * account, string payee)
{
trim(payee);
- context.journal.payees_for_unknown_accounts
+ context.journal->payees_for_unknown_accounts
.push_back(account_mapping_t(mask_t(payee), account));
}
void instance_t::account_default_directive(account_t * account)
{
- context.journal.bucket = account;
+ context.journal->bucket = account;
}
void instance_t::payee_directive(char * line)
{
- string payee = context.journal
- .register_payee(line, NULL, file_context(pathname, linenum));
+ string payee = context.journal->register_payee(line, NULL);
while (peek_whitespace_line()) {
read_line(line);
@@ -994,7 +968,7 @@ void instance_t::payee_directive(char * line)
void instance_t::payee_alias_directive(const string& payee, string alias)
{
trim(alias);
- context.journal.payee_mappings
+ context.journal->payee_mappings
.push_back(payee_mapping_t(mask_t(alias), payee));
}
@@ -1006,8 +980,7 @@ void instance_t::commodity_directive(char * line)
if (commodity_t * commodity =
commodity_pool_t::current_pool->find_or_create(symbol)) {
- context.journal.register_commodity(*commodity, 0,
- file_context(pathname, linenum));
+ context.journal->register_commodity(*commodity, 0);
while (peek_whitespace_line()) {
read_line(line);
@@ -1067,8 +1040,7 @@ void instance_t::commodity_default_directive(commodity_t& comm)
void instance_t::tag_directive(char * line)
{
char * p = skip_ws(line);
- context.journal.register_metadata(p, NULL_VALUE, 0,
- file_context(pathname, linenum));
+ context.journal->register_metadata(p, NULL_VALUE, 0);
while (peek_whitespace_line()) {
read_line(line);
@@ -1079,7 +1051,7 @@ void instance_t::tag_directive(char * line)
char * b = next_element(q);
string keyword(q);
if (keyword == "assert" || keyword == "check") {
- context.journal.tag_check_exprs.insert
+ context.journal->tag_check_exprs.insert
(tag_check_exprs_map::value_type
(string(p),
expr_t::check_expr_pair(expr_t(b),
@@ -1093,22 +1065,21 @@ void instance_t::tag_directive(char * line)
void instance_t::eval_directive(char * line)
{
expr_t expr(line);
- expr.calc(context.scope);
+ expr.calc(*context.scope);
}
void instance_t::assert_directive(char * line)
{
expr_t expr(line);
- if (! expr.calc(context.scope).to_boolean())
+ if (! expr.calc(*context.scope).to_boolean())
throw_(parse_error, _("Assertion failed: %1") << line);
}
void instance_t::check_directive(char * line)
{
expr_t expr(line);
- if (! expr.calc(context.scope).to_boolean())
- warning_(_("%1Check failed: %2")
- << file_context(pathname, linenum) << line);
+ if (! expr.calc(*context.scope).to_boolean())
+ context.warning(STR(_("Check failed: %1") << line));
}
void instance_t::comment_directive(char * line)
@@ -1242,12 +1213,12 @@ post_t * instance_t::parse_post(char * line,
post->xact = xact; // this could be NULL
post->pos = position_t();
- post->pos->pathname = pathname;
- post->pos->beg_pos = line_beg_pos;
- post->pos->beg_line = linenum;
+ post->pos->pathname = context.pathname;
+ post->pos->beg_pos = context.line_beg_pos;
+ post->pos->beg_line = context.linenum;
post->pos->sequence = context.sequence++;
- char buf[MAX_LINE + 1];
+ char buf[parse_context_t::MAX_LINE + 1];
std::strcpy(buf, line);
std::streamsize beg = 0;
@@ -1264,14 +1235,14 @@ post_t * instance_t::parse_post(char * line,
case '*':
post->set_state(item_t::CLEARED);
p = skip_ws(p + 1);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Parsed the CLEARED flag");
break;
case '!':
post->set_state(item_t::PENDING);
p = skip_ws(p + 1);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Parsed the PENDING flag");
break;
}
@@ -1294,25 +1265,23 @@ post_t * instance_t::parse_post(char * line,
if ((*p == '[' && *(e - 1) == ']') || (*p == '(' && *(e - 1) == ')')) {
post->add_flags(POST_VIRTUAL);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Parsed a virtual account name");
if (*p == '[') {
post->add_flags(POST_MUST_BALANCE);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Posting must balance");
}
p++; e--;
}
string name(p, static_cast<string::size_type>(e - p));
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Parsed account name " << name);
post->account =
- context.journal.register_account(name, post.get(),
- file_context(pathname, linenum),
- account);
+ context.journal->register_account(name, post.get(), account);
// Parse the optional amount
@@ -1323,16 +1292,15 @@ post_t * instance_t::parse_post(char * line,
if (*next != '(') // indicates a value expression
post->amount.parse(stream, PARSE_NO_REDUCE);
else
- parse_amount_expr(stream, context.scope, *post.get(), post->amount,
+ parse_amount_expr(stream, *context.scope, *post.get(), post->amount,
PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN,
defer_expr, &post->amount_expr);
if (! post->amount.is_null() && post->amount.has_commodity()) {
- context.journal.register_commodity(post->amount.commodity(), post.get(),
- file_context(pathname, linenum));
+ context.journal->register_commodity(post->amount.commodity(), post.get());
if (! post->amount.has_annotation()) {
- foreach (application_t& state, context.apply_stack) {
+ foreach (application_t& state, apply_stack) {
if (state.value.type() == typeid(fixed_rate_t)) {
fixed_rate_t& rate(boost::get<fixed_rate_t>(state.value));
if (*rate.first == post->amount.commodity()) {
@@ -1346,7 +1314,7 @@ post_t * instance_t::parse_post(char * line,
}
}
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "post amount = " << post->amount);
if (stream.eof()) {
@@ -1357,7 +1325,7 @@ post_t * instance_t::parse_post(char * line,
// Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST)
if (*next == '@') {
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Found a price indicator");
bool per_unit = true;
@@ -1365,7 +1333,7 @@ post_t * instance_t::parse_post(char * line,
if (*++next == '@') {
per_unit = false;
post->add_flags(POST_COST_IN_FULL);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "And it's for a total price");
}
@@ -1389,7 +1357,7 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression
post->cost->parse(cstream, PARSE_NO_MIGRATE);
else
- parse_amount_expr(cstream, context.scope, *post.get(), *post->cost,
+ parse_amount_expr(cstream, *context.scope, *post.get(), *post->cost,
PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN);
if (post->cost->sign() < 0)
@@ -1412,9 +1380,9 @@ post_t * instance_t::parse_post(char * line,
if (fixed_cost)
post->add_flags(POST_COST_FIXATED);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Total cost is " << *post->cost);
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Annotated amount is " << post->amount);
if (cstream.eof())
@@ -1431,7 +1399,7 @@ post_t * instance_t::parse_post(char * line,
// Parse the optional balance assignment
if (xact && next && *next == '=') {
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Found a balance assignment indicator");
beg = static_cast<std::streamsize>(++next - line);
@@ -1446,7 +1414,7 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression
post->assigned_amount->parse(stream, PARSE_NO_MIGRATE);
else
- parse_amount_expr(stream, context.scope, *post.get(),
+ parse_amount_expr(stream, *context.scope, *post.get(),
*post->assigned_amount,
PARSE_SINGLE | PARSE_NO_MIGRATE);
@@ -1457,17 +1425,17 @@ post_t * instance_t::parse_post(char * line,
throw parse_error(_("Balance assertion must evaluate to a constant"));
}
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "POST assign: parsed amt = " << *post->assigned_amount);
amount_t& amt(*post->assigned_amount);
value_t account_total
(post->account->amount().strip_annotations(keep_details_t()));
+ DEBUG("post.assign", "line " << context.linenum << ": "
+ << "account balance = " << account_total);
DEBUG("post.assign",
- "line " << linenum << ": " "account balance = " << account_total);
- DEBUG("post.assign",
- "line " << linenum << ": " "post amount = " << amt);
+ "line " << context.linenum << ": " << "post amount = " << amt);
amount_t diff = amt;
@@ -1487,9 +1455,9 @@ post_t * instance_t::parse_post(char * line,
}
DEBUG("post.assign",
- "line " << linenum << ": " << "diff = " << diff);
- DEBUG("textual.parse",
- "line " << linenum << ": " << "POST assign: diff = " << diff);
+ "line " << context.linenum << ": " << "diff = " << diff);
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "POST assign: diff = " << diff);
if (! diff.is_zero()) {
if (! post->amount.is_null()) {
@@ -1498,7 +1466,7 @@ post_t * instance_t::parse_post(char * line,
throw_(parse_error, _("Balance assertion off by %1") << diff);
} else {
post->amount = diff;
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Overwrite null posting");
}
}
@@ -1515,9 +1483,9 @@ post_t * instance_t::parse_post(char * line,
// Parse the optional note
if (next && *next == ';') {
- post->append_note(++next, context.scope, true);
+ post->append_note(++next, *context.scope, true);
next = line + len;
- DEBUG("textual.parse", "line " << linenum << ": "
+ DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Parsed a posting note");
}
@@ -1528,14 +1496,14 @@ post_t * instance_t::parse_post(char * line,
_("Unexpected char '%1' (Note: inline math requires parentheses)")
<< *next);
- post->pos->end_pos = curr_pos;
- post->pos->end_line = linenum;
+ post->pos->end_pos = context.curr_pos;
+ post->pos->end_line = context.linenum;
- if (! context.apply_stack.empty()) {
- foreach (const application_t& state, context.apply_stack)
+ if (! apply_stack.empty()) {
+ foreach (const application_t& state, apply_stack)
if (state.value.type() == typeid(string))
post->parse_tags(boost::get<string>(state.value).c_str(),
- context.scope, true);
+ *context.scope, true);
}
TRACE_STOP(post_details, 1);
@@ -1587,9 +1555,9 @@ xact_t * instance_t::parse_xact(char * line,
unique_ptr<xact_t> xact(new xact_t);
xact->pos = position_t();
- xact->pos->pathname = pathname;
- xact->pos->beg_pos = line_beg_pos;
- xact->pos->beg_line = linenum;
+ xact->pos->pathname = context.pathname;
+ xact->pos->beg_pos = context.line_beg_pos;
+ xact->pos->beg_line = context.linenum;
xact->pos->sequence = context.sequence++;
bool reveal_context = true;
@@ -1635,9 +1603,7 @@ xact_t * instance_t::parse_xact(char * line,
if (next && *next) {
char * p = next_element(next, true);
- xact->payee =
- context.journal.register_payee(next, xact.get(),
- file_context(pathname, linenum));
+ xact->payee = context.journal->register_payee(next, xact.get());
next = p;
} else {
xact->payee = _("<Unspecified payee>");
@@ -1646,7 +1612,7 @@ xact_t * instance_t::parse_xact(char * line,
// Parse the xact note
if (next && *next == ';')
- xact->append_note(++next, context.scope, false);
+ xact->append_note(++next, *context.scope, false);
TRACE_STOP(xact_text, 1);
@@ -1673,9 +1639,9 @@ xact_t * instance_t::parse_xact(char * line,
if (*p == ';') {
// This is a trailing note, and possibly a metadata info tag
- item->append_note(p + 1, context.scope, true);
+ item->append_note(p + 1, *context.scope, true);
item->add_flags(ITEM_NOTE_ON_NEXT_LINE);
- item->pos->end_pos = curr_pos;
+ item->pos->end_pos = context.curr_pos;
item->pos->end_line++;
}
else if ((remlen > 7 && *p == 'a' &&
@@ -1687,7 +1653,7 @@ xact_t * instance_t::parse_xact(char * line,
const char c = *p;
p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]);
expr_t expr(p);
- bind_scope_t bound_scope(context.scope, *item);
+ bind_scope_t bound_scope(*context.scope, *item);
if (c == 'e') {
expr.calc(bound_scope);
}
@@ -1695,8 +1661,7 @@ xact_t * instance_t::parse_xact(char * line,
if (c == 'a') {
throw_(parse_error, _("Transaction assertion failed: %1") << p);
} else {
- warning_(_("%1Transaction check failed: %2")
- << file_context(pathname, linenum) << p);
+ context.warning(STR(_("Transaction check failed: %1") << p));
}
}
}
@@ -1729,14 +1694,14 @@ xact_t * instance_t::parse_xact(char * line,
}
#endif
- xact->pos->end_pos = curr_pos;
- xact->pos->end_line = linenum;
+ xact->pos->end_pos = context.curr_pos;
+ xact->pos->end_line = context.linenum;
- if (! context.apply_stack.empty()) {
- foreach (const application_t& state, context.apply_stack)
+ if (! apply_stack.empty()) {
+ foreach (const application_t& state, apply_stack)
if (state.value.type() == typeid(string))
xact->parse_tags(boost::get<string>(state.value).c_str(),
- context.scope, false);
+ *context.scope, false);
}
TRACE_STOP(xact_details, 1);
@@ -1748,7 +1713,8 @@ xact_t * instance_t::parse_xact(char * line,
if (reveal_context) {
add_error_context(_("While parsing transaction:"));
add_error_context(source_context(xact->pos->pathname,
- xact->pos->beg_pos, curr_pos, "> "));
+ xact->pos->beg_pos,
+ context.curr_pos, "> "));
}
throw;
}
@@ -1757,26 +1723,18 @@ xact_t * instance_t::parse_xact(char * line,
expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
- return context.scope.lookup(kind, name);
+ return context.scope->lookup(kind, name);
}
-std::size_t journal_t::parse(std::istream& in,
- scope_t& scope,
- account_t * master_account,
- const path * original_file)
+std::size_t journal_t::read_textual(parse_context_stack_t& context_stack)
{
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
-
- parse_context_t context(*this, scope);
- if (master_account || this->master)
- context.apply_stack.push_front(application_t("account",
- master_account ?
- master_account : this->master));
-
- instance_t instance(context, in, original_file);
- instance.parse();
- context.close();
-
+ {
+ instance_t instance(context_stack, context_stack.get_current());
+ instance.apply_stack.push_front
+ (application_t("account", context_stack.get_current().master));
+ instance.parse();
+ }
TRACE_STOP(parsing_total, 1);
// These tracers were started in textual.cc
@@ -1787,10 +1745,10 @@ std::size_t journal_t::parse(std::istream& in,
TRACE_FINISH(instance_parse, 1); // report per-instance timers
TRACE_FINISH(parsing_total, 1);
- if (context.errors > 0)
- throw static_cast<int>(context.errors);
+ if (context_stack.get_current().errors > 0)
+ throw static_cast<int>(context_stack.get_current().errors);
- return context.count;
+ return context_stack.get_current().count;
}
} // namespace ledger
diff --git a/src/timelog.cc b/src/timelog.cc
index 8d3d69c1..67ea1015 100644
--- a/src/timelog.cc
+++ b/src/timelog.cc
@@ -36,14 +36,14 @@
#include "post.h"
#include "account.h"
#include "journal.h"
+#include "context.h"
namespace ledger {
namespace {
void clock_out_from_timelog(std::list<time_xact_t>& time_xacts,
time_xact_t out_event,
- journal_t& journal,
- scope_t& scope)
+ parse_context_t& context)
{
time_xact_t event;
@@ -95,7 +95,7 @@ namespace {
curr->pos = event.position;
if (! event.note.empty())
- curr->append_note(event.note.c_str(), scope);
+ curr->append_note(event.note.c_str(), *context.scope);
char buf[32];
std::sprintf(buf, "%lds", long((out_event.checkin - event.checkin)
@@ -110,7 +110,7 @@ namespace {
curr->add_post(post);
event.account->add_post(post);
- if (! journal.add_xact(curr.get()))
+ if (! context.journal->add_xact(curr.get()))
throw parse_error(_("Failed to record 'out' timelog transaction"));
else
curr.release();
@@ -129,9 +129,8 @@ void time_log_t::close()
DEBUG("timelog", "Clocking out from account " << account->fullname());
clock_out_from_timelog(time_xacts,
time_xact_t(none, CURRENT_TIME(), account),
- journal, scope);
- if (context_count)
- (*context_count)++;
+ context);
+ context.count++;
}
assert(time_xacts.empty());
}
@@ -154,7 +153,7 @@ void time_log_t::clock_out(time_xact_t event)
if (time_xacts.empty())
throw std::logic_error(_("Timelog check-out event without a check-in"));
- clock_out_from_timelog(time_xacts, event, journal, scope);
+ clock_out_from_timelog(time_xacts, event, context);
}
} // namespace ledger
diff --git a/src/timelog.h b/src/timelog.h
index 12083302..ed5a2d36 100644
--- a/src/timelog.h
+++ b/src/timelog.h
@@ -50,6 +50,7 @@ namespace ledger {
class account_t;
class journal_t;
+class parse_context_t;
class time_xact_t
{
@@ -86,15 +87,11 @@ public:
class time_log_t : public boost::noncopyable
{
std::list<time_xact_t> time_xacts;
- journal_t& journal;
- scope_t& scope;
+ parse_context_t& context;
public:
- std::size_t * context_count;
-
- time_log_t(journal_t& _journal, scope_t& _scope)
- : journal(_journal), scope(_scope), context_count(NULL) {
- TRACE_CTOR(time_log_t, "journal_t&, scope_t&, std::size&");
+ time_log_t(parse_context_t& _context) : context(_context) {
+ TRACE_CTOR(time_log_t, "parse_context_t&");
}
~time_log_t() {
TRACE_DTOR(time_log_t);
diff --git a/src/xact.cc b/src/xact.cc
index 0fedb42a..0251edde 100644
--- a/src/xact.cc
+++ b/src/xact.cc
@@ -35,6 +35,7 @@
#include "post.h"
#include "account.h"
#include "journal.h"
+#include "context.h"
#include "pool.h"
namespace ledger {
@@ -608,7 +609,7 @@ namespace {
}
}
-void auto_xact_t::extend_xact(xact_base_t& xact)
+void auto_xact_t::extend_xact(xact_base_t& xact, parse_context_t& context)
{
posts_list initial_posts(xact.posts.begin(), xact.posts.end());
@@ -674,7 +675,8 @@ void auto_xact_t::extend_xact(xact_base_t& xact)
throw_(parse_error,
_("Transaction assertion failed: %1") << pair.first);
else
- warning_(_("Transaction check failed: %1") << pair.first);
+ context.warning(STR(_("Transaction check failed: %1")
+ << pair.first));
}
}
}
diff --git a/src/xact.h b/src/xact.h
index cb7bdeb3..7d7fb826 100644
--- a/src/xact.h
+++ b/src/xact.h
@@ -49,6 +49,7 @@ namespace ledger {
class post_t;
class journal_t;
+class parse_context_t;
typedef std::list<post_t *> posts_list;
@@ -209,7 +210,7 @@ public:
deferred_notes->push_back(deferred_tag_data_t(p, overwrite_existing));
}
- virtual void extend_xact(xact_base_t& xact);
+ virtual void extend_xact(xact_base_t& xact, parse_context_t& context);
#if defined(HAVE_BOOST_SERIALIZATION)
private:
diff --git a/test/baseline/dir-tag.test b/test/baseline/dir-tag.test
index b1858146..496334a0 100644
--- a/test/baseline/dir-tag.test
+++ b/test/baseline/dir-tag.test
@@ -17,5 +17,5 @@ test reg
12-Feb-28 KFC food $20.00 $20.00
Assets:Cash $-20.00 0
__ERROR__
-Warning: Metadata check failed for (Happy: Summer): (value == "Valley")
+Warning: "/Users/johnw/Projects/ledger/test/baseline/dir-tag.test", line 8: Metadata check failed for (Happy: Summer): (value == "Valley")
end test
diff --git a/test/baseline/feat-check.test b/test/baseline/feat-check.test
index 9a2e72df..a9db1ec4 100644
--- a/test/baseline/feat-check.test
+++ b/test/baseline/feat-check.test
@@ -13,6 +13,6 @@ test bal
--------------------
0
__ERROR__
-Warning: Transaction check failed: (account =~ /Foo/)
+Warning: "$sourcepath/test/baseline/feat-check.test", line 6: Transaction check failed: (account =~ /Foo/)
Warning: "$sourcepath/test/baseline/feat-check.test", line 8: Check failed: account("Assets:Checking").all(account =~ /Expense/)
end test