Refactored the notion of "the current parsing context"
This commit is contained in:
parent
e2afc783db
commit
944e580825
23 changed files with 597 additions and 457 deletions
10
src/accum.h
10
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
|
||||
|
||||
|
|
|
|||
151
src/context.h
Normal file
151
src/context.h
Normal file
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
81
src/csv.cc
81
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;
|
||||
|
|
|
|||
33
src/csv.h
33
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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'")
|
||||
|
|
|
|||
139
src/journal.cc
139
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 = ¤t;
|
||||
|
||||
if (! scope)
|
||||
current.count = 0;
|
||||
if (! current.scope)
|
||||
current.scope = scope_t::default_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)
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
446
src/textual.cc
446
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:
|
||||
parse_context_stack_t& context_stack;
|
||||
parse_context_t& context;
|
||||
std::istream& in;
|
||||
instance_t * parent;
|
||||
|
||||
std::list<application_t> apply_stack;
|
||||
|
||||
journal_t& journal;
|
||||
scope_t& scope;
|
||||
#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);
|
||||
|
||||
context.apply_stack.pop_front();
|
||||
if (apply_stack.front().value.type() == typeid(optional<datetime_t>))
|
||||
epoch = boost::get<optional<datetime_t> >(apply_stack.front().value);
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue