Simplified error context handling.

This commit is contained in:
John Wiegley 2009-02-02 00:24:26 -04:00
parent ef3943c604
commit 9540406af1
19 changed files with 230 additions and 222 deletions

View file

@ -38,5 +38,5 @@ N $
; This is a transaction note!
; Sample: Another Value
; :MyTag:
Assets:Bank:Checking
Assets:Bank:Checking $-21.00
; :AnotherTag:

View file

@ -37,8 +37,7 @@
namespace ledger {
entry_base_t::entry_base_t(const entry_base_t& e)
: item_t(), journal(NULL)
entry_base_t::entry_base_t(const entry_base_t& e) : item_t()
{
TRACE_CTOR(entry_base_t, "copy");
#if 0
@ -80,11 +79,14 @@ item_t::state_t entry_base_t::state() const
void entry_base_t::add_xact(xact_t * xact)
{
xacts.push_back(xact);
xact->journal = journal;
}
bool entry_base_t::remove_xact(xact_t * xact)
{
xacts.remove(xact);
xact->entry = NULL;
xact->journal = NULL;
return true;
}
@ -226,10 +228,8 @@ bool entry_base_t::finalize()
DEBUG("entry.finalize", "final balance = " << balance);
if (! balance.is_null() && ! balance.is_zero()) {
#if 0
add_error_context(entry_context(*this));
#endif
add_error_context("Unbalanced remainder is: ");
add_error_context(item_context(*this));
add_error_context("Unbalanced remainder is:");
add_error_context(value_context(balance));
throw_(balance_error, "Entry does not balance");
}
@ -326,16 +326,6 @@ bool entry_t::valid() const
return true;
}
#if 0
void entry_context::describe(std::ostream& out) const throw()
{
if (! desc.empty())
out << desc << std::endl;
print_entry(out, entry, " ");
}
#endif
void auto_entry_t::extend_entry(entry_base_t& entry, bool post)
{
xacts_list initial_xacts(entry.xacts.begin(),

View file

@ -61,10 +61,9 @@ class journal_t;
class entry_base_t : public item_t
{
public:
journal_t * journal;
xacts_list xacts;
entry_base_t() : journal(NULL) {
entry_base_t() {
TRACE_CTOR(entry_base_t, "");
}
entry_base_t(const entry_base_t& e);

View file

@ -153,6 +153,11 @@ public:
std::ostream& operator<<(std::ostream& out, const expr_t& expr);
inline string expr_context(const expr_t& expr) {
// jww (2009-02-01): NYI
return "EXPR";
}
} // namespace ledger
#endif // _EXPR_H

View file

@ -201,10 +201,8 @@ void calc_xacts::operator()(xact_t& xact)
last_xact = &xact;
}
catch (const std::exception& err) {
add_error_context("Calculating transaction at");
#if 0
add_error_context(xact_context(xact));
#endif
add_error_context("While calculating transaction:");
add_error_context(item_context(xact));
throw;
}
}

View file

@ -298,7 +298,8 @@ void format_t::format(std::ostream& out_str, scope_t& scope)
value.strip_annotations().dump(out, elem->min_width);
}
catch (const calc_error&) {
out << (string("%") + elem->chars);
add_error_context("While calculating format expression:");
add_error_context(expr_context(elem->expr));
throw;
}
break;

View file

@ -35,8 +35,6 @@
namespace ledger {
bool item_t::use_effective_date = false;
bool item_t::has_tag(const string& tag) const
{
if (! metadata)
@ -213,6 +211,14 @@ value_t get_comment(item_t& item)
}
}
optional<date_t> item_t::date() const
{
if (session_t::current->report->use_effective_date && _date_eff)
return effective_date();
else
return actual_date();
}
expr_t::ptr_op_t item_t::lookup(const string& name)
{
switch (name[0]) {
@ -289,4 +295,41 @@ bool item_t::valid() const
return true;
}
string item_context(const item_t& item)
{
unsigned short x = 0;
foreach (const path& path, item.journal->sources) {
if (x++ == item.src_idx) {
std::size_t len = item.end_pos - item.beg_pos;
assert(len > 0);
assert(len < 2048);
ifstream in(path);
in.seekg(item.beg_pos, std::ios::beg);
scoped_array<char> buf(new char[len + 1]);
in.read(buf.get(), len);
std::ostringstream out;
out << "While balancing item from \"" << path.string()
<< "\", line " << item.beg_line
<< ", byte " << item.beg_pos << ":\n";
bool first = true;
for (char * p = std::strtok(buf.get(), "\n");
p;
p = std::strtok(NULL, "\n")) {
if (first)
first = false;
else
out << '\n';
out << " " << p;
return out.str();
}
}
assert(false);
return empty_string;
}
} // namespace ledger

View file

@ -51,6 +51,8 @@
namespace ledger {
class journal_t;
/**
* @brief Brief
*
@ -75,20 +77,25 @@ public:
typedef std::map<string, optional<string> > string_map;
optional<string_map> metadata;
journal_t * journal;
unsigned short src_idx;
#if 0
istream_pos_type full_beg_pos;
std::size_t full_beg_line;
#endif
istream_pos_type beg_pos;
std::size_t beg_line;
istream_pos_type end_pos;
std::size_t end_line;
#if 0
istream_pos_type full_end_pos;
std::size_t full_end_line;
static bool use_effective_date;
#endif
item_t(flags_t _flags = ITEM_NORMAL, const optional<string>& _note = none)
: supports_flags<>(_flags), _state(UNCLEARED), note(_note),
: supports_flags<>(_flags),
_state(UNCLEARED), note(_note), journal(NULL), src_idx(0),
beg_pos(0), beg_line(0), end_pos(0), end_line(0)
{
TRACE_CTOR(item_t, "flags_t, const string&");
@ -112,6 +119,8 @@ public:
note = item.note;
journal = item.journal;
src_idx = item.src_idx;
beg_pos = item.beg_pos;
beg_line = item.beg_line;
end_pos = item.end_pos;
@ -144,12 +153,7 @@ public:
virtual optional<date_t> effective_date() const {
return _date_eff;
}
optional<date_t> date() const {
if (use_effective_date && _date_eff)
return effective_date();
else
return actual_date();
}
optional<date_t> date() const;
void set_state(state_t new_state) {
_state = new_state;
@ -165,6 +169,8 @@ public:
value_t get_comment(item_t& item);
string item_context(const item_t& item);
} // namespace ledger
#endif // _ITEM_H

View file

@ -133,7 +133,7 @@ int main(int argc, char * argv[], char * envp[])
}
catch (const std::exception& err) {
std::cout.flush();
std::cerr << error_context() << "Error: " << err.what() << std::endl;
report_error(err);
}
catch (int _status) {
status = _status;

View file

@ -86,7 +86,7 @@ value_t expr_t::op_t::calc(scope_t& scope)
catch (const std::exception& err) {
if (context_op_ptr) {
add_error_context("While evaluating value expression:");
add_error_context(expr_context(this, context_op_ptr));
add_error_context(op_context(this, context_op_ptr));
}
throw;
}
@ -574,7 +574,7 @@ void expr_t::op_t::write(std::ostream& out) const
}
}
string expr_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t goal)
string op_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t goal)
{
ostream_pos_type start_pos, end_pos;
expr_t::op_t::context_t context(op, goal, &start_pos, &end_pos);
@ -588,7 +588,6 @@ string expr_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t goal)
else
buf << " ";
}
buf << '\n';
}
return buf.str();
}

View file

@ -332,7 +332,7 @@ inline expr_t::ptr_op_t expr_t::op_t::wrap_functor(const function_t& fobj) {
#define MAKE_FUNCTOR(x) expr_t::op_t::wrap_functor(bind(&x, this, _1))
#define WRAP_FUNCTOR(x) expr_t::op_t::wrap_functor(x)
string expr_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t op);
string op_context(const expr_t::ptr_op_t op, const expr_t::ptr_op_t op);
} // namespace ledger

View file

@ -75,7 +75,7 @@ namespace {
}
void process_option(const function_t& opt, scope_t& scope,
const char * arg)
const char * arg, const string& name)
{
try {
call_scope_t args(scope);
@ -85,23 +85,23 @@ namespace {
opt(args);
}
catch (const std::exception& err) {
#if 0
add_error_context("While parsing option '--" << opt->long_opt
<< "'" << (opt->short_opt != '\0' ?
(string(" (-") + opt->short_opt + "):") :
": "));
#endif
if (name[0] == '-')
add_error_context("While parsing option '" << name << "':");
else
add_error_context("While parsing environent variable '"
<< name << "':");
throw;
}
}
}
void process_option(const string& name, scope_t& scope,
const char * arg)
const char * arg, const string& varname)
{
op_bool_tuple opt(find_option(scope, name));
if (opt.get<0>())
process_option(opt.get<0>()->as_function(), scope, arg);
process_option(opt.get<0>()->as_function(), scope, arg, varname);
}
void process_environment(const char ** envp, const string& tag,
@ -126,7 +126,7 @@ void process_environment(const char ** envp, const string& tag,
if (*q == '=') {
try {
process_option(string(buf), scope, q + 1);
process_option(string(buf), scope, q + 1, string(*p, q - *p));
}
catch (const std::exception& err) {
add_error_context("While parsing environment variable option '"
@ -179,7 +179,8 @@ void process_arguments(int, char ** argv, scope_t& scope,
if (value == NULL)
throw_(option_error, "missing option argument for --" << name);
}
process_option(opt.get<0>()->as_function(), scope, value);
process_option(opt.get<0>()->as_function(), scope, value,
string("--") + name);
}
else if ((*i)[1] == '\0') {
throw_(option_error, "illegal option -");
@ -210,7 +211,8 @@ void process_arguments(int, char ** argv, scope_t& scope,
throw_(option_error,
"missing option argument for -" << o.get<2>());
}
process_option(o.get<0>()->as_function(), scope, value);
process_option(o.get<0>()->as_function(), scope, value,
string("-") + o.get<2>());
}
}
}

View file

@ -51,7 +51,7 @@
namespace ledger {
void process_option(const string& name, scope_t& scope,
const char * arg = NULL);
const char * arg, const string& name);
void process_environment(const char ** envp, const string& tag,
scope_t& scope);

View file

@ -149,6 +149,8 @@ public:
bool entry_sort;
bool sort_all;
bool anonymize;
bool use_effective_date;
string account;
optional<path> pager_path;
@ -184,6 +186,7 @@ public:
entry_sort(false),
sort_all(false),
anonymize(false),
use_effective_date(false),
raw_mode(false),
@ -274,7 +277,7 @@ public:
// Report filtering
value_t option_effective(call_scope_t& args) {
xact_t::use_effective_date = true;
use_effective_date = true;
}
#endif

View file

@ -116,7 +116,11 @@ namespace {
#endif
if (expr) {
amount = expr.calc(*xact).as_amount();
value_t result(expr.calc(*xact));
// jww (2009-02-01): What about storing time-dependent expressions?
if (! result.is_amount())
throw_(parse_error, "Transactions may only specify simple amounts");
amount = result.as_amount();
DEBUG("textual.parse", "The transaction amount is " << amount);
return expr;
}
@ -182,7 +186,8 @@ void textual_parser_t::instance_t::parse()
try {
read_next_directive();
beg_pos = end_pos;
beg_pos = end_pos;
beg_line = linenum;
}
catch (const std::exception& err) {
if (parent) {
@ -194,16 +199,14 @@ void textual_parser_t::instance_t::parse()
instances.push_front(instance);
foreach (instance_t * instance, instances)
add_error_context("In file included from '"
add_error_context("In file included from "
<< file_context(instance->pathname,
instance->linenum - 1) << "':");
instance->linenum - 1));
}
add_error_context("While parsing file "
<< file_context(pathname, linenum - 1) << "\n");
<< file_context(pathname, linenum - 1));
std::cout.flush();
std::cerr << ledger::error_context()
<< "Error: " << err.what() << std::endl;
report_error(err);
errors++;
}
}
@ -446,7 +449,7 @@ void textual_parser_t::instance_t::option_directive(char * line)
if (p)
*p++ = '\0';
}
process_option(line + 2, session, p);
process_option(line + 2, session, p, line);
}
void textual_parser_t::instance_t::automated_entry_directive(char * line)
@ -501,13 +504,15 @@ void textual_parser_t::instance_t::entry_directive(char * line)
// possibility that add_entry ma throw an exception, which
// would cause us to leak without this guard.
std::auto_ptr<entry_t> entry_ptr(entry);
entry->src_idx = src_idx;
entry->beg_pos = beg_pos;
entry->beg_line = beg_line;
entry->end_pos = pos;
entry->end_line = linenum;
if (journal.add_entry(entry)) {
entry_ptr.release(); // it's owned by the journal now
entry->src_idx = src_idx;
entry->beg_pos = beg_pos;
entry->beg_line = beg_line;
entry->end_pos = pos;
entry->end_line = linenum;
count++;
}
// It's perfectly valid for the journal to reject the entry,
@ -653,6 +658,9 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
{
std::istringstream in(line);
istream_pos_type beg = in.tellg();
istream_pos_type end = beg;
string err_desc;
try {
@ -683,22 +691,22 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
// Parse the account name
istream_pos_type account_beg = in.tellg();
istream_pos_type account_end = account_beg;
beg = in.tellg();
end = beg;
while (! in.eof()) {
in.get(p);
if (in.eof() || (std::isspace(p) &&
(p == '\t' || in.peek() == EOF ||
std::isspace(in.peek()))))
break;
account_end += 1;
end += 1;
}
if (account_beg == account_end)
if (beg == end)
throw parse_error("No account was specified");
char * b = &line[long(account_beg)];
char * e = &line[long(account_end)];
char * b = &line[long(beg)];
char * e = &line[long(end)];
if ((*b == '[' && *(e - 1) == ']') ||
(*b == '(' && *(e - 1) == ')')) {
@ -738,34 +746,28 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
if (p == '=' && entry)
goto parse_assign;
try {
istream_pos_type beg = in.tellg();
beg = in.tellg();
xact->amount_expr =
parse_amount_expr(in, xact->amount, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) |
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
saw_amount = true;
xact->amount_expr =
parse_amount_expr(in, xact->amount, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) |
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
saw_amount = true;
if (! xact->amount.is_null()) {
xact->amount.reduce();
DEBUG("textual.parse", "line " << linenum << ": " <<
"Reduced amount is " << xact->amount);
}
// We don't need to store the actual expression that resulted in the
// amount if it's constant
if (xact->amount_expr) {
if (xact->amount_expr->is_constant())
xact->amount_expr = expr_t();
istream_pos_type end = in.tellg();
xact->amount_expr->set_text(string(line, long(beg), long(end - beg)));
}
if (! xact->amount.is_null()) {
xact->amount.reduce();
DEBUG("textual.parse", "line " << linenum << ": " <<
"Reduced amount is " << xact->amount);
}
catch (const std::exception& err) {
add_error_context("While parsing transaction amount:\n");
throw;
// We don't need to store the actual expression that resulted in the
// amount if it's constant
if (xact->amount_expr) {
if (xact->amount_expr->is_constant())
xact->amount_expr = expr_t();
end = in.tellg();
xact->amount_expr->set_text(string(line, long(beg), long(end - beg)));
}
}
@ -792,27 +794,21 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
if (in.good() && ! in.eof()) {
xact->cost = amount_t();
try {
istream_pos_type beg = in.tellg();
beg = in.tellg();
xact->cost_expr =
parse_amount_expr(in, *xact->cost, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE) |
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
xact->cost_expr =
parse_amount_expr(in, *xact->cost, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE) |
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
if (xact->cost_expr) {
istream_pos_type end = in.tellg();
if (per_unit)
xact->cost_expr->set_text(string("@") +
string(line, long(beg), long(end - beg)));
else
xact->cost_expr->set_text(string("@@") +
string(line, long(beg), long(end - beg)));
}
}
catch (const std::exception& err) {
add_error_context("While parsing transaction cost:\n");
throw;
if (xact->cost_expr) {
end = in.tellg();
if (per_unit)
xact->cost_expr->set_text(string("@") +
string(line, long(beg), long(end - beg)));
else
xact->cost_expr->set_text(string("@@") +
string(line, long(beg), long(end - beg)));
}
if (xact->cost->sign() < 0)
@ -850,81 +846,75 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
if (in.good() && ! in.eof()) {
xact->assigned_amount = amount_t();
try {
istream_pos_type beg = in.tellg();
beg = in.tellg();
xact->assigned_amount_expr =
parse_amount_expr(in, *xact->assigned_amount, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE));
xact->assigned_amount_expr =
parse_amount_expr(in, *xact->assigned_amount, xact.get(),
static_cast<uint_least8_t>(expr_t::PARSE_NO_MIGRATE));
if (xact->assigned_amount->is_null())
throw parse_error
("An assigned balance must evaluate to a constant value");
if (xact->assigned_amount->is_null())
throw parse_error
("An assigned balance must evaluate to a constant value");
DEBUG("textual.parse", "line " << linenum << ": " <<
"XACT assign: parsed amt = " << *xact->assigned_amount);
DEBUG("textual.parse", "line " << linenum << ": " <<
"XACT assign: parsed amt = " << *xact->assigned_amount);
if (xact->assigned_amount_expr) {
istream_pos_type end = in.tellg();
xact->assigned_amount_expr->set_text
(string("=") + string(line, long(beg), long(end - beg)));
}
account_t::xdata_t& xdata(xact->account->xdata());
amount_t& amt(*xact->assigned_amount);
DEBUG("xact.assign",
"account balance = " << xdata.value.strip_annotations());
DEBUG("xact.assign",
"xact amount = " << amt.strip_annotations());
amount_t diff;
if (xdata.value.is_amount()) {
diff = amt - xdata.value.as_amount();
}
else if (xdata.value.is_balance()) {
if (optional<amount_t> comm_bal =
xdata.value.as_balance().commodity_amount(amt.commodity()))
diff = amt - *comm_bal;
else
diff = amt;
}
else if (xdata.value.is_balance_pair()) {
if (optional<amount_t> comm_bal =
xdata.value.as_balance_pair().commodity_amount(amt.commodity()))
diff = amt - *comm_bal;
else
diff = amt;
}
else {
diff = amt;
}
DEBUG("xact.assign", "diff = " << diff.strip_annotations());
DEBUG("textual.parse", "line " << linenum << ": " <<
"XACT assign: diff = " << diff.strip_annotations());
if (! diff.is_zero()) {
if (! xact->amount.is_null()) {
diff -= xact->amount;
if (! diff.is_zero()) {
xact_t * temp = new xact_t(xact->account, diff,
ITEM_GENERATED | XACT_CALCULATED);
entry->add_xact(temp);
DEBUG("textual.parse", "line " << linenum << ": " <<
"Created balancing transaction");
}
} else {
xact->amount = diff;
DEBUG("textual.parse", "line " << linenum << ": " <<
"Overwrite null transaction");
}
}
if (xact->assigned_amount_expr) {
end = in.tellg();
xact->assigned_amount_expr->set_text
(string("=") + string(line, long(beg), long(end - beg)));
}
catch (const std::exception& err) {
add_error_context("While parsing assigned balance:\n");
throw;
account_t::xdata_t& xdata(xact->account->xdata());
amount_t& amt(*xact->assigned_amount);
DEBUG("xact.assign",
"account balance = " << xdata.value.strip_annotations());
DEBUG("xact.assign",
"xact amount = " << amt.strip_annotations());
amount_t diff;
if (xdata.value.is_amount()) {
diff = amt - xdata.value.as_amount();
}
else if (xdata.value.is_balance()) {
if (optional<amount_t> comm_bal =
xdata.value.as_balance().commodity_amount(amt.commodity()))
diff = amt - *comm_bal;
else
diff = amt;
}
else if (xdata.value.is_balance_pair()) {
if (optional<amount_t> comm_bal =
xdata.value.as_balance_pair().commodity_amount(amt.commodity()))
diff = amt - *comm_bal;
else
diff = amt;
}
else {
diff = amt;
}
DEBUG("xact.assign", "diff = " << diff.strip_annotations());
DEBUG("textual.parse", "line " << linenum << ": " <<
"XACT assign: diff = " << diff.strip_annotations());
if (! diff.is_zero()) {
if (! xact->amount.is_null()) {
diff -= xact->amount;
if (! diff.is_zero()) {
xact_t * temp = new xact_t(xact->account, diff,
ITEM_GENERATED | XACT_CALCULATED);
entry->add_xact(temp);
DEBUG("textual.parse", "line " << linenum << ": " <<
"Created balancing transaction");
}
} else {
xact->amount = diff;
DEBUG("textual.parse", "line " << linenum << ": " <<
"Overwrite null transaction");
}
}
}
}
@ -967,8 +957,8 @@ xact_t * textual_parser_t::instance_t::parse_xact(char * line,
}
catch (const std::exception& err) {
add_error_context("While parsing transaction:\n");
add_error_context(line_context(line, in.tellg()));
add_error_context("While parsing transaction:");
add_error_context(line_context(line, beg, in.tellg()));
throw;
}
}

View file

@ -642,18 +642,6 @@ void finish_timer(const char * name)
#endif // LOGGING_ON && TIMERS_ON
/**********************************************************************
*
* Exception handling
*/
namespace ledger {
std::ostringstream _desc_buffer;
std::ostringstream _ctxt_buffer;
} // namespace ledger
/**********************************************************************
*
* General utility functions

View file

@ -973,7 +973,6 @@ inline string value_context(const value_t& val) {
buf << std::right;
buf.width(20);
val.print(buf);
buf << std::endl;
return buf.str();
}

View file

@ -101,13 +101,13 @@ void read_environment_settings(report_t& report, char * envp[])
// These are here for backwards compatability, but are deprecated.
if (const char * p = std::getenv("LEDGER"))
process_option("file", report, p);
process_option("file", report, p, "LEDGER");
if (const char * p = std::getenv("LEDGER_INIT"))
process_option("init-file", report, p);
process_option("init-file", report, p, "LEDGER_INIT");
if (const char * p = std::getenv("PRICE_HIST"))
process_option("price-db", report, p);
process_option("price-db", report, p, "PRICE_HIST");
if (const char * p = std::getenv("PRICE_EXP"))
process_option("price-exp", report, p);
process_option("price-exp", report, p, "PRICE_EXP");
#endif
TRACE_FINISH(environment, 1);

View file

@ -234,21 +234,6 @@ bool xact_t::valid() const
return true;
}
#if 0
xact_context::xact_context(const xact_t& _xact, const string& desc) throw()
: file_context("", 0, desc), xact(_xact)
{
const paths_list& sources(xact.entry->journal->sources);
std::size_t x = 0;
foreach (const path& path, sources)
if (x == xact.entry->src_idx) {
file = path;
break;
}
line = xact.beg_line;
}
#endif
void xact_t::add_to_value(value_t& value)
{
if (xdata_ && xdata_->has_flags(XACT_EXT_COMPOUND)) {