summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am69
-rw-r--r--amount.cc3
-rw-r--r--binary.cc633
-rw-r--r--binary.h20
-rw-r--r--commodity.cc5
-rw-r--r--csv.cc12
-rw-r--r--expr.cc159
-rw-r--r--expr.h114
-rw-r--r--format.cc28
-rw-r--r--format.h6
-rw-r--r--gnucash.h4
-rw-r--r--journal.h61
-rw-r--r--ledger.h12
-rw-r--r--main.cc28
-rw-r--r--ofx.h4
-rw-r--r--op.cc1151
-rw-r--r--op.h324
-rw-r--r--option.cc24
-rw-r--r--option.h9
-rw-r--r--parser.cc445
-rw-r--r--parser.h201
-rw-r--r--predicate.h74
-rw-r--r--pyinterp.cc16
-rw-r--r--pyinterp.h14
-rw-r--r--qif.h4
-rw-r--r--report.cc12
-rw-r--r--report.h48
-rw-r--r--scope.cc136
-rw-r--r--scope.h374
-rw-r--r--session.cc26
-rw-r--r--session.h41
-rw-r--r--test/numerics/t_expr.cc25
-rw-r--r--test/numerics/t_expr.h30
-rw-r--r--textual.cc40
-rw-r--r--textual.h4
-rw-r--r--token.cc398
-rw-r--r--token.h119
-rw-r--r--utils.h74
-rw-r--r--walk.cc14
-rw-r--r--walk.h26
-rw-r--r--xml.h4
41 files changed, 4082 insertions, 709 deletions
diff --git a/Makefile.am b/Makefile.am
index beaea2f9..e1aaa45c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -38,23 +38,28 @@ endif
libledger_la_CPPFLAGS = $(libamounts_la_CPPFLAGS)
libledger_la_SOURCES = \
- binary.cc \
- csv.cc \
- derive.cc \
- emacs.cc \
+ mask.cc \
+ token.cc \
+ parser.cc \
+ op.cc \
+ expr.cc \
+ scope.cc \
format.cc \
+ \
journal.cc \
- mask.cc \
- option.cc \
- parsexp.cc \
- qif.cc \
- reconcile.cc \
report.cc \
session.cc \
- textual.cc \
- valexpr.cc \
walk.cc \
- xml.cc
+ \
+ textual.cc \
+ binary.cc \
+ csv.cc \
+ emacs.cc \
+ qif.cc \
+ xml.cc \
+ derive.cc \
+ reconcile.cc \
+ option.cc
# quotes.cc this is currently not being included
if HAVE_EXPAT
libledger_la_SOURCES += gnucash.cc
@@ -78,28 +83,36 @@ pkginclude_HEADERS = \
value.h \
times.h \
utils.h \
+ error.h \
+ \
+ expr.h \
+ op.h \
+ parser.h \
+ token.h \
+ scope.h \
+ predicate.h \
+ format.h \
+ mask.h \
\
+ journal.h \
+ report.h \
+ session.h \
+ walk.h \
+ \
+ textual.h \
binary.h \
csv.h \
- derive.h \
emacs.h \
- error.h \
- format.h \
gnucash.h \
- journal.h \
- ledger.h \
- mask.h \
- option.h \
- parser.h \
- parsexp.h \
qif.h \
- quotes.h \
+ xml.h \
+ \
+ derive.h \
reconcile.h \
- report.h \
- textual.h \
- valexpr.h \
- walk.h \
- xml.h
+ quotes.h \
+ option.h \
+ \
+ ledger.h
if USE_PCH
nodist_libledger_la_SOURCES = system.hh.gch
@@ -173,7 +186,7 @@ nodist_UnitTests_SOURCES = test/UnitTests.cc \
test/numerics/t_commodity.cc \
test/numerics/t_amount.cc \
test/numerics/t_balance.cc \
- test/numerics/t_valexpr.cc
+ test/numerics/t_expr.cc
UnitTests_CPPFLAGS = -I$(srcdir)/test $(libledger_la_CPPFLAGS)
UnitTests_LDFLAGS = $(LIBADD_DL)
diff --git a/amount.cc b/amount.cc
index c87fc38d..4ed30087 100644
--- a/amount.cc
+++ b/amount.cc
@@ -42,7 +42,6 @@
*/
#include "amount.h"
-#include "parser.h"
#include "binary.h"
namespace ledger {
@@ -1245,7 +1244,7 @@ void amount_t::print(std::ostream& _out, bool omit_commodity,
#if 0
-// jww (2008-05-08): Should these be global?
+// jww (2008-07-29): Should these be static?
namespace {
#endif
char * bigints;
diff --git a/binary.cc b/binary.cc
index b30b180b..17d33a96 100644
--- a/binary.cc
+++ b/binary.cc
@@ -31,6 +31,7 @@
#include "binary.h"
#include "journal.h"
+#include "session.h"
namespace ledger {
@@ -58,7 +59,7 @@ extern char * bigints_next;
extern unsigned int bigints_index;
extern unsigned int bigints_count;
-bool binary_parser_t::test(std::istream& in) const
+bool journal_t::binary_parser_t::test(std::istream& in) const
{
if (binary::read_number_nocheck<unsigned long>(in) == binary_magic_number &&
binary::read_number_nocheck<unsigned long>(in) == format_version)
@@ -76,14 +77,13 @@ namespace binary {
account_t * master);
}
-unsigned int binary_parser_t::parse(std::istream& in,
- session_t& session,
- journal_t& journal,
- account_t * master,
- const path * original_file)
+unsigned int journal_t::binary_parser_t::parse(std::istream& in,
+ session_t& session,
+ journal_t& journal,
+ account_t * master,
+ const path * original_file)
{
- return binary::read_journal(in, original_file ? *original_file : "",
- journal, master);
+ return journal.read(in, original_file ? *original_file : "", master);
}
namespace binary {
@@ -281,67 +281,17 @@ inline void read_value(const char *& data, value_t& val)
}
}
-inline void read_mask(const char *& data, mask_t *& mask)
+inline void read_mask(const char *& data, mask_t& mask)
{
bool exclude;
read_number(data, exclude);
string pattern;
read_string(data, pattern);
- mask = new mask_t(pattern);
- mask->exclude = exclude;
+ mask = mask_t(pattern);
+ mask.exclude = exclude;
}
-inline expr::ptr_op_t read_value_expr(const char *& data)
-{
- if (! read_bool(data))
- return expr::ptr_op_t();
-
- expr::op_t::kind_t kind;
- read_number(data, kind);
-
- expr::ptr_op_t expr = new expr::op_t(kind);
-
- if (kind > expr::op_t::TERMINALS)
- expr->set_left(read_value_expr(data));
-
- switch (expr->kind) {
- case expr::op_t::O_ARG:
- case expr::op_t::INDEX: {
- long temp;
- read_long(data, temp);
- expr->set_long(temp);
- break;
- }
- case expr::op_t::VALUE: {
- value_t temp;
- read_value(data, temp);
- expr->set_value(temp);
- break;
- }
-
- case expr::op_t::F_CODE_MASK:
- case expr::op_t::F_PAYEE_MASK:
- case expr::op_t::F_NOTE_MASK:
- case expr::op_t::F_ACCOUNT_MASK:
- case expr::op_t::F_SHORT_ACCOUNT_MASK:
- case expr::op_t::F_COMMODITY_MASK:
-#if 0
- if (read_bool(data))
- read_mask(data, expr->mask);
-#endif
- break;
-
- default:
- if (kind > expr::op_t::TERMINALS)
- expr->set_right(read_value_expr(data));
- break;
- }
-
- return expr;
-}
-
-
inline void read_transaction(const char *& data, transaction_t * xact)
{
read_number(data, xact->_date);
@@ -354,24 +304,20 @@ inline void read_transaction(const char *& data, transaction_t * xact)
}
else if (flag == 1) {
read_amount(data, xact->amount);
- read_string(data, xact->amount_expr.expr_str);
+ string str;
+ read_string(data, str);
+ xact->amount_expr.set_text(str);
}
else {
- expr::ptr_op_t ptr = read_value_expr(data);
- assert(ptr.get());
- xact->amount_expr.reset(ptr);
- read_string(data, xact->amount_expr.expr_str);
+ xact->amount_expr.read(data);
}
if (read_bool(data)) {
xact->cost = amount_t();
read_amount(data, *xact->cost);
- expr::ptr_op_t ptr = read_value_expr(data);
- assert(ptr.get());
- value_expr expr;
- expr.reset(ptr);
- xact->cost_expr = expr;
+ xact->cost_expr = expr_t();
+ xact->cost_expr->read(data);
} else {
xact->cost = none;
}
@@ -388,8 +334,10 @@ inline void read_transaction(const char *& data, transaction_t * xact)
xact->data = NULL;
+#if 0
if (xact->amount_expr)
- expr::compute_amount(xact->amount_expr.get(), xact->amount, xact);
+ expr_t::compute_amount(xact->amount_expr.get(), xact->amount, xact);
+#endif
}
inline void read_entry_base(const char *& data, entry_base_t * entry,
@@ -429,7 +377,10 @@ inline void read_auto_entry(const char *& data, auto_entry_t * entry,
{
bool ignore;
read_entry_base(data, entry, xact_pool, ignore);
- entry->predicate = item_predicate<transaction_t>(read_value_expr(data));
+
+ expr_t expr;
+ expr.read(data);
+ entry->predicate = item_predicate<transaction_t>(expr);
}
inline void read_period_entry(const char *& data, period_entry_t * entry,
@@ -597,218 +548,6 @@ account_t * read_account(const char *& data, journal_t& journal,
return acct;
}
-unsigned int read_journal(std::istream& in,
- const path& file,
- journal_t& journal,
- account_t * master)
-{
- account_index =
- base_commodity_index =
- commodity_index = 0;
-
- // Read in the files that participated in this journal, so that they
- // can be checked for changes on reading.
-
- if (! file.empty()) {
- for (unsigned short i = 0,
- count = read_number<unsigned short>(in);
- i < count;
- i++) {
- path pathname = read_string(in);
- std::time_t old_mtime;
- read_number(in, old_mtime);
- struct stat info;
- // jww (2008-04-22): can this be done differently now?
- stat(pathname.string().c_str(), &info);
- if (std::difftime(info.st_mtime, old_mtime) > 0)
- return 0;
-
- journal.sources.push_back(pathname);
- }
-
- // Make sure that the cache uses the same price database,
- // otherwise it means that LEDGER_PRICE_DB has been changed, and
- // we should ignore this cache file.
- if (read_bool(in)) {
- string pathname;
- read_string(in, pathname);
- if (! journal.price_db ||
- journal.price_db->string() != std::string(pathname))
- return 0;
- }
- }
-
- // Read all of the data in at once, so that we're just dealing with
- // a big data buffer.
-
- unsigned long data_size = read_number<unsigned long>(in);
-
- char * data_pool = new char[data_size];
- in.read(data_pool, data_size);
-
- // Read in the accounts
-
- const char * data = data_pool;
-
- account_t::ident_t a_count = read_long<account_t::ident_t>(data);
- accounts = accounts_next = new account_t *[a_count];
-
- assert(journal.master);
- checked_delete(journal.master);
- journal.master = read_account(data, journal, master);
-
- if (read_bool(data))
- journal.basket = accounts[read_long<account_t::ident_t>(data) - 1];
-
- // Allocate the memory needed for the entries and transactions in
- // one large block, which is then chopped up and custom constructed
- // as necessary.
-
- unsigned long count = read_long<unsigned long>(data);
- unsigned long auto_count = read_long<unsigned long>(data);
- unsigned long period_count = read_long<unsigned long>(data);
- unsigned long xact_count = read_number<unsigned long>(data);
- unsigned long bigint_count = read_number<unsigned long>(data);
-
- std::size_t pool_size = (sizeof(entry_t) * count +
- sizeof(transaction_t) * xact_count +
- amount_t::sizeof_bigint_t() * bigint_count);
-
- char * item_pool = new char[pool_size];
-
- journal.item_pool = item_pool;
- journal.item_pool_end = item_pool + pool_size;
-
- entry_t * entry_pool = (entry_t *) item_pool;
- transaction_t * xact_pool = (transaction_t *) (item_pool +
- sizeof(entry_t) * count);
- bigints_index = 0;
- bigints = bigints_next = (item_pool + sizeof(entry_t) * count +
- sizeof(transaction_t) * xact_count);
-
- // Read in the base commodities and then derived commodities
-
- commodity_t::ident_t bc_count = read_long<commodity_t::ident_t>(data);
- base_commodities = base_commodities_next = new commodity_t::base_t *[bc_count];
-
- for (commodity_t::ident_t i = 0; i < bc_count; i++) {
-#if 0
- commodity_t::base_t * base = read_commodity_base(data);
-
- // jww (2008-04-22): How does the pool get created here?
- amount_t::current_pool->commodities.push_back(commodity);
-
- // jww (2008-04-22): What about this logic here?
- if (! result.second) {
- base_commodities_map::iterator c =
- commodity_t::base_t::commodities.find(commodity->symbol);
-
- // It's possible the user might have used a commodity in a value
- // expression passed to an option, we'll just override the
- // flags, but keep the commodity pointer intact.
- if (c == commodity_t::base_t::commodities.end())
- throw new error(string("Failed to read base commodity from cache: ") +
- commodity->symbol);
-
- (*c).second->name = commodity->name;
- (*c).second->note = commodity->note;
- (*c).second->precision = commodity->precision;
- (*c).second->flags = commodity->flags;
- if ((*c).second->smaller)
- checked_delete((*c).second->smaller);
- (*c).second->smaller = commodity->smaller;
- if ((*c).second->larger)
- checked_delete((*c).second->larger);
- (*c).second->larger = commodity->larger;
-
- *(base_commodities_next - 1) = (*c).second;
- checked_delete(commodity);
- }
-#endif
- }
-
- commodity_t::ident_t c_count = read_long<commodity_t::ident_t>(data);
- commodities = commodities_next = new commodity_t *[c_count];
-
- for (commodity_t::ident_t i = 0; i < c_count; i++) {
- commodity_t * commodity;
- string mapping_key;
-
- if (! read_bool(data)) {
- commodity = read_commodity(data);
- mapping_key = commodity->base->symbol;
- } else {
- read_string(data, mapping_key);
- commodity = read_commodity_annotated(data);
- }
-
- // jww (2008-04-22): What do I do with mapping_key here?
- amount_t::current_pool->commodities.push_back(commodity);
-#if 0
- // jww (2008-04-22): What about the error case?
- if (! result.second) {
- commodities_map::iterator c =
- commodity_t::commodities.find(mapping_key);
- if (c == commodity_t::commodities.end())
- throw new error(string("Failed to read commodity from cache: ") +
- commodity->symbol());
-
- *(commodities_next - 1) = (*c).second;
- checked_delete(commodity);
- }
-#endif
- }
-
- for (commodity_t::ident_t i = 0; i < bc_count; i++)
- read_commodity_base_extra(data, i);
-
- commodity_t::ident_t ident;
- read_long(data, ident);
- if (ident == 0xffffffff || ident == 0)
- amount_t::current_pool->default_commodity = NULL;
- else
- amount_t::current_pool->default_commodity = commodities[ident - 1];
-
- // Read in the entries and transactions
-
- for (unsigned long i = 0; i < count; i++) {
- new(entry_pool) entry_t;
- bool finalize = false;
- read_entry(data, entry_pool, xact_pool, finalize);
- entry_pool->journal = &journal;
- if (finalize && ! entry_pool->finalize())
- continue;
- journal.entries.push_back(entry_pool++);
- }
-
- for (unsigned long i = 0; i < auto_count; i++) {
- auto_entry_t * auto_entry = new auto_entry_t;
- read_auto_entry(data, auto_entry, xact_pool);
- auto_entry->journal = &journal;
- journal.auto_entries.push_back(auto_entry);
- }
-
- for (unsigned long i = 0; i < period_count; i++) {
- period_entry_t * period_entry = new period_entry_t;
- bool finalize = false;
- read_period_entry(data, period_entry, xact_pool, finalize);
- period_entry->journal = &journal;
- if (finalize && ! period_entry->finalize())
- continue;
- journal.period_entries.push_back(period_entry);
- }
-
- // Clean up and return the number of entries read
-
- checked_array_delete(accounts);
- checked_array_delete(commodities);
- checked_array_delete(data_pool);
-
- VERIFY(journal.valid());
-
- return count;
-}
-
void write_amount(std::ostream& out, const amount_t& amt)
{
if (amt.commodity_)
@@ -844,60 +583,14 @@ void write_value(std::ostream& out, const value_t& val)
}
}
-void write_mask(std::ostream& out, mask_t * mask)
-{
- write_number(out, mask->exclude);
- write_string(out, mask->expr.str());
-}
-
-void write_value_expr(std::ostream& out, const expr::ptr_op_t expr)
+void write_mask(std::ostream& out, mask_t& mask)
{
- if (! expr) {
- write_bool(out, false);
- return;
- }
-
- write_bool(out, true);
- write_number(out, expr->kind);
-
- if (expr->kind > expr::op_t::TERMINALS)
- write_value_expr(out, expr->left());
-
- switch (expr->kind) {
- case expr::op_t::O_ARG:
- case expr::op_t::INDEX:
- write_long(out, expr->as_long());
- break;
- case expr::op_t::VALUE:
- write_value(out, expr->as_value());
- break;
-
- case expr::op_t::F_CODE_MASK:
- case expr::op_t::F_PAYEE_MASK:
- case expr::op_t::F_NOTE_MASK:
- case expr::op_t::F_ACCOUNT_MASK:
- case expr::op_t::F_SHORT_ACCOUNT_MASK:
- case expr::op_t::F_COMMODITY_MASK:
-#if 0
- if (expr->mask) {
- write_bool(out, true);
- write_mask(out, expr->mask);
- } else {
- write_bool(out, false);
- }
-#endif
- break;
-
- default:
- if (expr->kind > expr::op_t::TERMINALS)
- write_value_expr(out, expr->right());
- break;
- }
-
+ write_number(out, mask.exclude);
+ write_string(out, mask.expr.str());
}
void write_transaction(std::ostream& out, transaction_t * xact,
- bool ignore_calculated)
+ bool ignore_calculated)
{
write_number(out, xact->_date);
write_number(out, xact->_date_eff);
@@ -909,13 +602,12 @@ void write_transaction(std::ostream& out, transaction_t * xact,
}
else if (xact->amount_expr) {
write_number<unsigned char>(out, 2);
- write_value_expr(out, xact->amount_expr.get());
- write_string(out, xact->amount_expr.expr_str);
+ xact->amount_expr.write(out);
}
- else if (! xact->amount_expr.expr_str.empty()) {
+ else if (! xact->amount_expr.text().empty()) {
write_number<unsigned char>(out, 1);
write_amount(out, xact->amount);
- write_string(out, xact->amount_expr.expr_str);
+ write_string(out, xact->amount_expr.text());
}
else {
write_number<unsigned char>(out, 0);
@@ -926,7 +618,7 @@ void write_transaction(std::ostream& out, transaction_t * xact,
(! (ignore_calculated && xact->has_flags(TRANSACTION_CALCULATED)))) {
write_bool(out, true);
write_amount(out, *xact->cost);
- write_string(out, xact->cost_expr->expr_str);
+ xact->cost_expr->write(out);
} else {
write_bool(out, false);
}
@@ -979,7 +671,7 @@ void write_entry(std::ostream& out, entry_t * entry)
void write_auto_entry(std::ostream& out, auto_entry_t * entry)
{
write_entry_base(out, entry);
- write_value_expr(out, entry->predicate.predicate.get());
+ entry->predicate.predicate.write(out);
}
void write_period_entry(std::ostream& out, period_entry_t * entry)
@@ -1102,8 +794,226 @@ void write_account(std::ostream& out, account_t * account)
write_account(out, (*i).second);
}
-void write_journal(std::ostream& out, journal_t& journal)
+} // namespace binary
+
+unsigned int journal_t::read(std::istream& in,
+ const path& file,
+ account_t * master)
+{
+ using namespace binary;
+
+ account_index =
+ base_commodity_index =
+ commodity_index = 0;
+
+ // Read in the files that participated in this journal, so that they
+ // can be checked for changes on reading.
+
+ if (! file.empty()) {
+ for (unsigned short i = 0,
+ count = read_number<unsigned short>(in);
+ i < count;
+ i++) {
+ path pathname = read_string(in);
+ std::time_t old_mtime;
+ read_number(in, old_mtime);
+ struct stat info;
+ // jww (2008-04-22): can this be done differently now?
+ stat(pathname.string().c_str(), &info);
+ if (std::difftime(info.st_mtime, old_mtime) > 0)
+ return 0;
+
+ sources.push_back(pathname);
+ }
+
+ // Make sure that the cache uses the same price database,
+ // otherwise it means that LEDGER_PRICE_DB has been changed, and
+ // we should ignore this cache file.
+ if (read_bool(in)) {
+ string pathname;
+ read_string(in, pathname);
+ if (! price_db ||
+ price_db->string() != std::string(pathname))
+ return 0;
+ }
+ }
+
+ // Read all of the data in at once, so that we're just dealing with
+ // a big data buffer.
+
+ unsigned long data_size = read_number<unsigned long>(in);
+
+ char * data_pool = new char[data_size];
+ in.read(data_pool, data_size);
+
+ // Read in the accounts
+
+ const char * data = data_pool;
+
+ account_t::ident_t a_count = read_long<account_t::ident_t>(data);
+ accounts = accounts_next = new account_t *[a_count];
+
+ // jww (2008-07-29): Does this still apply?
+ assert(owner->master);
+ checked_delete(owner->master);
+ owner->master = read_account(data, *this, master);
+
+ if (read_bool(data))
+ basket = accounts[read_long<account_t::ident_t>(data) - 1];
+
+ // Allocate the memory needed for the entries and transactions in
+ // one large block, which is then chopped up and custom constructed
+ // as necessary.
+
+ unsigned long count = read_long<unsigned long>(data);
+ unsigned long auto_count = read_long<unsigned long>(data);
+ unsigned long period_count = read_long<unsigned long>(data);
+ unsigned long xact_count = read_number<unsigned long>(data);
+ unsigned long bigint_count = read_number<unsigned long>(data);
+
+ std::size_t pool_size = (sizeof(entry_t) * count +
+ sizeof(transaction_t) * xact_count +
+ amount_t::sizeof_bigint_t() * bigint_count);
+
+ char * item_pool = new char[pool_size];
+
+ item_pool = item_pool;
+ item_pool_end = item_pool + pool_size;
+
+ entry_t * entry_pool = (entry_t *) item_pool;
+ transaction_t * xact_pool = (transaction_t *) (item_pool +
+ sizeof(entry_t) * count);
+ bigints_index = 0;
+ bigints = bigints_next = (item_pool + sizeof(entry_t) * count +
+ sizeof(transaction_t) * xact_count);
+
+ // Read in the base commodities and then derived commodities
+
+ commodity_t::ident_t bc_count = read_long<commodity_t::ident_t>(data);
+ base_commodities = base_commodities_next = new commodity_t::base_t *[bc_count];
+
+ for (commodity_t::ident_t i = 0; i < bc_count; i++) {
+#if 0
+ commodity_t::base_t * base = read_commodity_base(data);
+
+ // jww (2008-04-22): How does the pool get created here?
+ amount_t::current_pool->commodities.push_back(commodity);
+
+ // jww (2008-04-22): What about this logic here?
+ if (! result.second) {
+ base_commodities_map::iterator c =
+ commodity_t::base_t::commodities.find(commodity->symbol);
+
+ // It's possible the user might have used a commodity in a value
+ // expression passed to an option, we'll just override the
+ // flags, but keep the commodity pointer intact.
+ if (c == commodity_t::base_t::commodities.end())
+ throw new error(string("Failed to read base commodity from cache: ") +
+ commodity->symbol);
+
+ (*c).second->name = commodity->name;
+ (*c).second->note = commodity->note;
+ (*c).second->precision = commodity->precision;
+ (*c).second->flags = commodity->flags;
+ if ((*c).second->smaller)
+ checked_delete((*c).second->smaller);
+ (*c).second->smaller = commodity->smaller;
+ if ((*c).second->larger)
+ checked_delete((*c).second->larger);
+ (*c).second->larger = commodity->larger;
+
+ *(base_commodities_next - 1) = (*c).second;
+ checked_delete(commodity);
+ }
+#endif
+ }
+
+ commodity_t::ident_t c_count = read_long<commodity_t::ident_t>(data);
+ commodities = commodities_next = new commodity_t *[c_count];
+
+ for (commodity_t::ident_t i = 0; i < c_count; i++) {
+ commodity_t * commodity;
+ string mapping_key;
+
+ if (! read_bool(data)) {
+ commodity = read_commodity(data);
+ mapping_key = commodity->base->symbol;
+ } else {
+ read_string(data, mapping_key);
+ commodity = read_commodity_annotated(data);
+ }
+
+ // jww (2008-04-22): What do I do with mapping_key here?
+ amount_t::current_pool->commodities.push_back(commodity);
+#if 0
+ // jww (2008-04-22): What about the error case?
+ if (! result.second) {
+ commodities_map::iterator c =
+ commodity_t::commodities.find(mapping_key);
+ if (c == commodity_t::commodities.end())
+ throw new error(string("Failed to read commodity from cache: ") +
+ commodity->symbol());
+
+ *(commodities_next - 1) = (*c).second;
+ checked_delete(commodity);
+ }
+#endif
+ }
+
+ for (commodity_t::ident_t i = 0; i < bc_count; i++)
+ read_commodity_base_extra(data, i);
+
+ commodity_t::ident_t ident;
+ read_long(data, ident);
+ if (ident == 0xffffffff || ident == 0)
+ amount_t::current_pool->default_commodity = NULL;
+ else
+ amount_t::current_pool->default_commodity = commodities[ident - 1];
+
+ // Read in the entries and transactions
+
+ for (unsigned long i = 0; i < count; i++) {
+ new(entry_pool) entry_t;
+ bool finalize = false;
+ read_entry(data, entry_pool, xact_pool, finalize);
+ entry_pool->journal = this;
+ if (finalize && ! entry_pool->finalize())
+ continue;
+ entries.push_back(entry_pool++);
+ }
+
+ for (unsigned long i = 0; i < auto_count; i++) {
+ auto_entry_t * auto_entry = new auto_entry_t;
+ read_auto_entry(data, auto_entry, xact_pool);
+ auto_entry->journal = this;
+ auto_entries.push_back(auto_entry);
+ }
+
+ for (unsigned long i = 0; i < period_count; i++) {
+ period_entry_t * period_entry = new period_entry_t;
+ bool finalize = false;
+ read_period_entry(data, period_entry, xact_pool, finalize);
+ period_entry->journal = this;
+ if (finalize && ! period_entry->finalize())
+ continue;
+ period_entries.push_back(period_entry);
+ }
+
+ // Clean up and return the number of entries read
+
+ checked_array_delete(accounts);
+ checked_array_delete(commodities);
+ checked_array_delete(data_pool);
+
+ VERIFY(valid());
+
+ return count;
+}
+
+void journal_t::write(std::ostream& out)
{
+ using namespace binary;
+
account_index =
base_commodity_index =
commodity_index = 0;
@@ -1114,12 +1024,12 @@ void write_journal(std::ostream& out, journal_t& journal)
// Write out the files that participated in this journal, so that
// they can be checked for changes on reading.
- if (journal.sources.empty()) {
+ if (sources.empty()) {
write_number<unsigned short>(out, 0);
} else {
- write_number<unsigned short>(out, journal.sources.size());
- for (paths_list::const_iterator i = journal.sources.begin();
- i != journal.sources.end();
+ write_number<unsigned short>(out, sources.size());
+ for (paths_list::const_iterator i = sources.begin();
+ i != sources.end();
i++) {
write_string(out, (*i).string());
struct stat info;
@@ -1129,9 +1039,9 @@ void write_journal(std::ostream& out, journal_t& journal)
// Write out the price database that relates to this data file, so
// that if it ever changes the cache can be invalidated.
- if (journal.price_db) {
+ if (price_db) {
write_bool(out, true);
- write_string(out, journal.price_db->string());
+ write_string(out, price_db->string());
} else {
write_bool(out, false);
}
@@ -1142,21 +1052,21 @@ void write_journal(std::ostream& out, journal_t& journal)
// Write out the accounts
- write_long<account_t::ident_t>(out, count_accounts(journal.master));
- write_account(out, journal.master);
+ write_long<account_t::ident_t>(out, count_accounts(master));
+ write_account(out, master);
- if (journal.basket) {
+ if (basket) {
write_bool(out, true);
- write_long(out, journal.basket->ident);
+ write_long(out, basket->ident);
} else {
write_bool(out, false);
}
// Write out the number of entries, transactions, and amounts
- write_long<unsigned long>(out, journal.entries.size());
- write_long<unsigned long>(out, journal.auto_entries.size());
- write_long<unsigned long>(out, journal.period_entries.size());
+ write_long<unsigned long>(out, entries.size());
+ write_long<unsigned long>(out, auto_entries.size());
+ write_long<unsigned long>(out, period_entries.size());
ostream_pos_type xacts_val = out.tellp();
write_number<unsigned long>(out, 0);
@@ -1220,22 +1130,22 @@ void write_journal(std::ostream& out, journal_t& journal)
unsigned long xact_count = 0;
- for (entries_list::const_iterator i = journal.entries.begin();
- i != journal.entries.end();
+ for (entries_list::const_iterator i = entries.begin();
+ i != entries.end();
i++) {
write_entry(out, *i);
xact_count += (*i)->transactions.size();
}
- for (auto_entries_list::const_iterator i = journal.auto_entries.begin();
- i != journal.auto_entries.end();
+ for (auto_entries_list::const_iterator i = auto_entries.begin();
+ i != auto_entries.end();
i++) {
write_auto_entry(out, *i);
xact_count += (*i)->transactions.size();
}
- for (period_entries_list::const_iterator i = journal.period_entries.begin();
- i != journal.period_entries.end();
+ for (period_entries_list::const_iterator i = period_entries.begin();
+ i != period_entries.end();
i++) {
write_period_entry(out, *i);
xact_count += (*i)->transactions.size();
@@ -1254,5 +1164,4 @@ void write_journal(std::ostream& out, journal_t& journal)
write_number<unsigned long>(out, bigints_count);
}
-} // namespace binary
} // namespace ledger
diff --git a/binary.h b/binary.h
index 3509e490..1c09a27b 100644
--- a/binary.h
+++ b/binary.h
@@ -32,12 +32,9 @@
#ifndef BINARY_H
#define BINARY_H
-#include "parser.h"
+#include "utils.h"
namespace ledger {
-
-class journal_t;
-
namespace binary {
template <typename T>
@@ -272,22 +269,7 @@ inline void write_object(std::ostream& out, const T& journal) {
assert(false);
}
-void write_journal(std::ostream& out, journal_t& journal);
-
} // namespace binary
-
-class binary_parser_t : public parser_t
-{
- public:
- virtual bool test(std::istream& in) const;
-
- virtual unsigned int parse(std::istream& in,
- session_t& session,
- journal_t& journal,
- account_t * master = NULL,
- const path * original_file = NULL);
-};
-
} // namespace ledger
#endif // BINARY_H
diff --git a/commodity.cc b/commodity.cc
index 0a62a861..173ebbc8 100644
--- a/commodity.cc
+++ b/commodity.cc
@@ -40,7 +40,6 @@
*/
#include "amount.h"
-#include "parser.h" // for parsing utility functions
namespace ledger {
@@ -248,7 +247,7 @@ void commodity_t::parse_symbol(char *& p, string& symbol)
if (*p == '"') {
char * q = std::strchr(p + 1, '"');
if (! q)
- throw_(parse_error, "Quoted commodity symbol lacks closing quote");
+ throw_(amount_error, "Quoted commodity symbol lacks closing quote");
symbol = string(p + 1, 0, q - p - 1);
p = q + 2;
} else {
@@ -260,7 +259,7 @@ void commodity_t::parse_symbol(char *& p, string& symbol)
p += symbol.length();
}
if (symbol.empty())
- throw_(parse_error, "Failed to parse commodity");
+ throw_(amount_error, "Failed to parse commodity");
}
bool commodity_t::valid() const
diff --git a/csv.cc b/csv.cc
index d3c3faf7..1a0ed2f9 100644
--- a/csv.cc
+++ b/csv.cc
@@ -25,7 +25,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%D");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << ',';
@@ -33,7 +35,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%P");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << ',';
@@ -41,7 +45,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%A");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << ',';
@@ -49,7 +55,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%t");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << ',';
@@ -57,7 +65,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%T");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << ',';
@@ -94,7 +104,9 @@ void format_csv_transactions::operator()(transaction_t& xact)
{
format_t fmt("%N");
std::ostringstream str;
+#if 0
fmt.format(str, details_t(xact));
+#endif
write_escaped_string(out, str.str());
}
out << '\n';
diff --git a/expr.cc b/expr.cc
new file mode 100644
index 00000000..0319e243
--- /dev/null
+++ b/expr.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#include "expr.h"
+#include "parser.h"
+#include "op.h"
+
+namespace ledger {
+
+std::auto_ptr<expr_t::parser_t> expr_t::parser;
+
+expr_t::expr_t()
+{
+ TRACE_CTOR(expr_t, "");
+}
+
+expr_t::expr_t(const expr_t& other) : ptr(other.ptr), str(other.str)
+{
+ TRACE_CTOR(expr_t, "copy");
+}
+
+expr_t::expr_t(const string& _str, const unsigned int flags) : str(_str)
+{
+ TRACE_CTOR(expr_t, "const string&");
+
+ if (! _str.empty())
+ ptr = parser->parse(str, flags);
+}
+
+ expr_t::expr_t(std::istream& in, const unsigned int flags)
+{
+ TRACE_CTOR(expr_t, "std::istream&");
+
+ ptr = parser->parse(in, flags);
+}
+
+expr_t::expr_t(const ptr_op_t& _ptr, const string& _str)
+ : ptr(_ptr), str(_str)
+{
+ TRACE_CTOR(expr_t, "const ptr_op_t&, const string&");
+}
+
+expr_t::~expr_t() throw()
+{
+ TRACE_DTOR(expr_t);
+}
+
+expr_t& expr_t::operator=(const expr_t& _expr)
+{
+ str = _expr.str;
+ ptr = _expr.ptr;
+ return *this;
+}
+
+void expr_t::parse(const string& _str, const unsigned int flags)
+{
+ if (! parser.get())
+ throw_(parse_error, "Value expression parser not initialized");
+
+ str = _str;
+ ptr = parser->parse(str, flags);
+}
+
+void expr_t::parse(std::istream& in, const unsigned int flags)
+{
+ if (! parser.get())
+ throw_(parse_error, "Value expression parser not initialized");
+
+ str = "<stream>";
+ ptr = parser->parse(in, flags);
+}
+
+void expr_t::compile(scope_t& scope)
+{
+ if (ptr.get())
+ ptr = ptr->compile(scope);
+}
+
+value_t expr_t::calc(scope_t& scope) const
+{
+ if (ptr.get())
+ return ptr->calc(scope);
+ return NULL_VALUE;
+}
+
+value_t expr_t::eval(const string& _expr, scope_t& scope)
+{
+ return expr_t(_expr).calc(scope);
+}
+
+void expr_t::print(std::ostream& out, scope_t& scope) const
+{
+ op_t::print_context_t context(scope);
+ ptr->print(out, context);
+}
+
+void expr_t::dump(std::ostream& out) const
+{
+ if (ptr)
+ ptr->dump(out, 0);
+}
+
+void expr_t::read(std::ostream& in)
+{
+ if (ptr)
+ ptr->read(in);
+}
+
+void expr_t::read(const char *& data)
+{
+ if (ptr)
+ ptr->read(data);
+}
+
+void expr_t::write(std::ostream& out) const
+{
+ if (ptr)
+ ptr->write(out);
+}
+
+void expr_t::initialize()
+{
+ parser.reset(new expr_t::parser_t);
+}
+
+void expr_t::shutdown()
+{
+ parser.reset();
+}
+
+} // namespace ledger
diff --git a/expr.h b/expr.h
new file mode 100644
index 00000000..4e415496
--- /dev/null
+++ b/expr.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _EXPR_H
+#define _EXPR_H
+
+#include "value.h"
+
+namespace ledger {
+
+DECLARE_EXCEPTION(error, parse_error);
+DECLARE_EXCEPTION(error, compile_error);
+DECLARE_EXCEPTION(error, calc_error);
+
+class scope_t;
+class call_scope_t;
+
+typedef function<value_t (call_scope_t&)> function_t;
+
+class expr_t
+{
+ struct token_t;
+
+ class parser_t;
+ static std::auto_ptr<parser_t> parser;
+
+public:
+ class op_t;
+ typedef intrusive_ptr<op_t> ptr_op_t;
+
+private:
+ ptr_op_t ptr;
+ string str;
+
+ static void initialize();
+ static void shutdown();
+
+ friend class session_t;
+
+public:
+ expr_t();
+ expr_t(const expr_t& other);
+ expr_t(const ptr_op_t& _ptr, const string& _str = "");
+
+ expr_t(const string& _str, const unsigned int flags = 0);
+ expr_t(std::istream& in, const unsigned int flags = 0);
+
+ virtual ~expr_t() throw();
+
+ expr_t& operator=(const expr_t& _expr);
+ expr_t& operator=(const string& _expr) {
+ parse(_expr);
+ return *this;
+ }
+
+ operator bool() const throw() {
+ return ptr.get() != NULL;
+ }
+ string text() const throw() {
+ return str;
+ }
+
+ // This has special use in the textual parser
+ void set_text(const string& txt) {
+ str = txt;
+ }
+
+ void parse(const string& _str, const unsigned int flags = 0);
+ void parse(std::istream& in, const unsigned int flags = 0);
+
+ void compile(scope_t& scope);
+ value_t calc(scope_t& scope) const;
+
+ void print(std::ostream& out, scope_t& scope) const;
+ void dump(std::ostream& out) const;
+
+ void read(std::ostream& in);
+ void read(const char *& data);
+ void write(std::ostream& out) const;
+
+ static value_t eval(const string& _expr, scope_t& scope);
+};
+
+} // namespace ledger
+
+#endif // _EXPR_H
diff --git a/format.cc b/format.cc
index bfcb81ba..4615afa0 100644
--- a/format.cc
+++ b/format.cc
@@ -219,7 +219,7 @@ element_t * format_t::parse_elements(const string& fmt)
current->type = element_t::VALUE_EXPR;
assert(! current->val_expr);
- current->val_expr = string(b, p);
+ current->val_expr.parse(string(b, p));
break;
}
@@ -320,7 +320,7 @@ namespace {
}
}
-void format_t::format(std::ostream& out_str, const details_t& details) const
+void format_t::format(std::ostream& out_str, const scope_t& scope) const
{
for (const element_t * elem = elements; elem; elem = elem->next) {
std::ostringstream out;
@@ -340,10 +340,11 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
out << elem->chars;
break;
+#if 0
case element_t::AMOUNT:
case element_t::TOTAL:
case element_t::VALUE_EXPR: {
- value_expr * calc;
+ expr_t * calc;
switch (elem->type) {
case element_t::AMOUNT:
assert(value_expr::amount_expr.get());
@@ -747,6 +748,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
out << " ";
}
break;
+#endif
default:
assert(false);
@@ -779,6 +781,7 @@ format_transactions::format_transactions(std::ostream& _output_stream,
void format_transactions::operator()(transaction_t& xact)
{
+#if 0
if (! transaction_has_xdata(xact) ||
! (transaction_xdata_(xact).dflags & TRANSACTION_DISPLAYED)) {
if (last_entry != xact.entry) {
@@ -795,10 +798,12 @@ void format_transactions::operator()(transaction_t& xact)
transaction_xdata(xact).dflags |= TRANSACTION_DISPLAYED;
last_xact = &xact;
}
+#endif
}
void format_entries::format_last_entry()
{
+#if 0
bool first = true;
for (transactions_list::const_iterator i = last_entry->transactions.begin();
i != last_entry->transactions.end();
@@ -814,6 +819,7 @@ void format_entries::format_last_entry()
transaction_xdata_(**i).dflags |= TRANSACTION_DISPLAYED;
}
}
+#endif
}
void format_entries::operator()(transaction_t& xact)
@@ -838,7 +844,7 @@ void print_entry(std::ostream& out, const entry_base_t& entry_base,
}
else if (const auto_entry_t * entry =
dynamic_cast<const auto_entry_t *>(&entry_base)) {
- out << "= " << entry->predicate.predicate.expr_str << '\n';
+ out << "= " << entry->predicate.predicate.text() << '\n';
print_format = prefix + " %-34A %12o\n";
}
else if (const period_entry_t * entry =
@@ -867,14 +873,17 @@ bool disp_subaccounts_p(const account_t& account,
const account_t *& to_show)
{
bool display = false;
+#if 0
unsigned int counted = 0;
bool matches = disp_pred ? (*disp_pred)(account) : true;
- value_t acct_total;
bool computed = false;
+#endif
+ value_t acct_total;
value_t result;
to_show = NULL;
+#if 0
for (accounts_map::const_iterator i = account.accounts.begin();
i != account.accounts.end();
i++) {
@@ -894,6 +903,7 @@ bool disp_subaccounts_p(const account_t& account,
to_show = (*i).second;
counted++;
}
+#endif
return display;
}
@@ -922,6 +932,7 @@ bool display_account(const account_t& account,
void format_accounts::operator()(account_t& account)
{
+#if 0
if (display_account(account, disp_pred)) {
if (! account.parent) {
account_xdata(account).dflags |= ACCOUNT_TO_DISPLAY;
@@ -930,6 +941,7 @@ void format_accounts::operator()(account_t& account)
account_xdata(account).dflags |= ACCOUNT_DISPLAYED;
}
}
+#endif
}
format_equity::format_equity(std::ostream& _output_stream,
@@ -937,6 +949,7 @@ format_equity::format_equity(std::ostream& _output_stream,
const string& display_predicate)
: output_stream(_output_stream), disp_pred(display_predicate)
{
+#if 0
const char * f = _format.c_str();
if (const char * p = std::strstr(f, "%/")) {
first_line_format.reset(string(f, 0, p - f));
@@ -950,10 +963,12 @@ format_equity::format_equity(std::ostream& _output_stream,
header_entry.payee = "Opening Balances";
header_entry._date = current_moment;
first_line_format.format(output_stream, details_t(header_entry));
+#endif
}
void format_equity::flush()
{
+#if 0
account_xdata_t xdata;
xdata.value = total;
xdata.value.negate();
@@ -980,10 +995,12 @@ void format_equity::flush()
next_lines_format.format(output_stream, details_t(summary));
}
output_stream.flush();
+#endif
}
void format_equity::operator()(account_t& account)
{
+#if 0
if (display_account(account, disp_pred)) {
if (account_has_xdata(account)) {
value_t val = account_xdata_(account).value;
@@ -1011,6 +1028,7 @@ void format_equity::operator()(account_t& account)
}
account_xdata(account).dflags |= ACCOUNT_DISPLAYED;
}
+#endif
}
} // namespace ledger
diff --git a/format.h b/format.h
index 405da2e1..eb735180 100644
--- a/format.h
+++ b/format.h
@@ -2,7 +2,7 @@
#define _FORMAT_H
#include "journal.h"
-#include "valexpr.h"
+#include "expr.h"
#include "walk.h"
namespace ledger {
@@ -53,7 +53,7 @@ struct element_t : public noncopyable
string chars;
unsigned char min_width;
unsigned char max_width;
- value_expr val_expr;
+ expr_t val_expr;
struct element_t * next;
@@ -110,7 +110,7 @@ struct format_t : public noncopyable
static string truncate(const string& str, unsigned int width,
const bool is_account = false);
- void format(std::ostream& out, const details_t& details) const;
+ void format(std::ostream& out, const scope_t& scope) const;
};
class format_transactions : public item_handler<transaction_t>
diff --git a/gnucash.h b/gnucash.h
index 25703d91..b8c880cc 100644
--- a/gnucash.h
+++ b/gnucash.h
@@ -1,11 +1,11 @@
#ifndef _GNUCASH_H
#define _GNUCASH_H
-#include "parser.h"
+#include "journal.h"
namespace ledger {
-class gnucash_parser_t : public parser_t
+class gnucash_parser_t : public journal_t::parser_t
{
public:
virtual bool test(std::istream& in) const;
diff --git a/journal.h b/journal.h
index c5a58d74..2248714e 100644
--- a/journal.h
+++ b/journal.h
@@ -34,8 +34,8 @@
#include "amount.h"
#include "value.h"
-#include "valexpr.h"
-#include "utils.h"
+#include "expr.h"
+#include "predicate.h"
namespace ledger {
@@ -62,9 +62,9 @@ class transaction_t : public supports_flags<>
optional<datetime_t> _date;
optional<datetime_t> _date_eff;
amount_t amount;
- value_expr amount_expr;
+ expr_t amount_expr;
optional<amount_t> cost;
- optional<value_expr> cost_expr;
+ optional<expr_t> cost_expr;
optional<string> note;
istream_pos_type beg_pos;
unsigned long beg_line;
@@ -452,7 +452,7 @@ class session_t;
class journal_t : public noncopyable
{
- public:
+public:
session_t * owner;
account_t * master;
account_t * basket;
@@ -488,6 +488,57 @@ class journal_t : public noncopyable
}
bool valid() const;
+
+/**
+ * @class journal_t::parser_t
+ *
+ * @brief Provides an abstract interface for writing journal parsers.
+ *
+ * Any data format for Ledger data is possible, as long as it can be parsed
+ * into a journal_t data tree. This class provides the basic interface which
+ * must be implemented by every such journal parser.
+ */
+ class parser_t : public noncopyable
+ {
+ public:
+ parser_t() {
+ TRACE_CTOR(parser_t, "");
+ }
+ virtual ~parser_t() {
+ TRACE_DTOR(parser_t);
+ }
+
+ virtual bool test(std::istream& in) const = 0;
+
+ virtual unsigned int parse(std::istream& in,
+ session_t& session,
+ journal_t& journal,
+ account_t * master = NULL,
+ const path * original_file = NULL) = 0;
+ };
+
+ class binary_parser_t : public parser_t
+ {
+ public:
+ virtual bool test(std::istream& in) const;
+
+ virtual unsigned int parse(std::istream& in,
+ session_t& session,
+ journal_t& journal,
+ account_t * master = NULL,
+ const path * original_file = NULL);
+ };
+
+ unsigned int read(std::istream& in, const path& file, account_t * master);
+ void write(std::ostream& out);
+
+ class parse_error : public error
+ {
+ public:
+ parse_error(const string& reason, error_context * ctxt = NULL) throw()
+ : error(reason, ctxt) {}
+ virtual ~parse_error() throw() {}
+ };
};
inline void extend_entry_base(journal_t * journal, entry_base_t& entry,
diff --git a/ledger.h b/ledger.h
index 03a77609..5c63b541 100644
--- a/ledger.h
+++ b/ledger.h
@@ -51,8 +51,7 @@
#include <emacs.h>
#include <csv.h>
//#include <quotes.h>
-#include <valexpr.h>
-#include <parsexp.h>
+#include <expr.h>
#include <walk.h>
#include <derive.h>
#include <reconcile.h>
@@ -67,15 +66,6 @@
#include <qif.h>
#include <ofx.h>
-namespace ledger {
- extern parser_t * binary_parser_ptr;
- extern parser_t * xml_parser_ptr;
- extern parser_t * gnucash_parser_ptr;
- extern parser_t * ofx_parser_ptr;
- extern parser_t * qif_parser_ptr;
- extern parser_t * textual_parser_ptr;
-}
-
#include <session.h>
#include <report.h>
diff --git a/main.cc b/main.cc
index 5646b032..c79f54dd 100644
--- a/main.cc
+++ b/main.cc
@@ -46,10 +46,10 @@
#endif
namespace ledger {
- value_t register_command(expr::call_scope_t& args)
+ value_t register_command(call_scope_t& args)
{
- expr::var_t<report_t> report(args, 0);
- expr::var_t<std::ostream> ostream(args, 1);
+ var_t<report_t> report(args, 0);
+ var_t<std::ostream> ostream(args, 1);
report->transactions_report
(xact_handler_ptr(new format_transactions
@@ -168,9 +168,8 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
string verb = *arg++;
if (verb == "parse") {
- value_expr expr(*arg);
+ expr_t expr(*arg);
-#if 0
IF_INFO() {
std::cout << "Value expression tree:" << std::endl;
expr.dump(std::cout);
@@ -194,7 +193,6 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
}
std::cout << expr.calc(report).strip_annotations() << std::endl;
-#endif
return 0;
}
@@ -227,28 +225,26 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
// Are we handling the expr commands? Do so now.
if (verb == "expr") {
- value_expr expr(*arg);
+ expr_t expr(*arg);
-#if 0
IF_INFO() {
*out << "Value expression tree:" << std::endl;
expr.dump(*out);
*out << std::endl;
*out << "Value expression parsed was:" << std::endl;
- expr.print(*out, doc_scope);
+ expr.print(*out, report);
*out << std::endl << std::endl;
*out << "Result of calculation: ";
}
- *out << expr.calc(doc_scope).strip_annotations() << std::endl;
-#endif
+ *out << expr.calc(report).strip_annotations() << std::endl;
return 0;
}
// Read the command word and create a command object based on it
- expr::function_t command;
+ function_t command;
if (verb == "register" || verb == "reg" || verb == "r")
command = register_command;
@@ -285,7 +281,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
std::strcpy(buf, "command_");
std::strcat(buf, verb.c_str());
- if (expr::ptr_op_t def = report.lookup(buf))
+ if (expr_t::ptr_op_t def = report.lookup(buf))
command = def->as_function();
if (! command)
@@ -295,7 +291,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
// Create an argument scope containing the report command's
// arguments, and then invoke the command.
- expr::call_scope_t command_args(report);
+ call_scope_t command_args(report);
command_args.push_back(value_t(&report));
command_args.push_back(value_t(out));
@@ -320,7 +316,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
TRACE_START(binary_cache, 1, "Wrote binary journal file");
ofstream stream(*session.cache_file);
- binary::write_journal(stream, journal);
+ journal.write(stream);
TRACE_FINISH(binary_cache, 1);
}
@@ -400,7 +396,7 @@ int main(int argc, char * argv[], char * envp[])
ledger::set_session_context(session.get());
- session->register_parser(new ledger::binary_parser_t);
+ session->register_parser(new ledger::journal_t::binary_parser_t);
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
session->register_parser(new ledger::xml_parser_t);
session->register_parser(new ledger::gnucash_parser_t);
diff --git a/ofx.h b/ofx.h
index aff2c65a..3e51fdcc 100644
--- a/ofx.h
+++ b/ofx.h
@@ -1,11 +1,11 @@
#ifndef _OFX_H
#define _OFX_H
-#include "parser.h"
+#include "journal.h"
namespace ledger {
-class ofx_parser_t : public parser_t
+class ofx_parser_t : public journal_t::parser_t
{
public:
virtual bool test(std::istream& in) const;
diff --git a/op.cc b/op.cc
new file mode 100644
index 00000000..abb91915
--- /dev/null
+++ b/op.cc
@@ -0,0 +1,1151 @@
+/*
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#include "op.h"
+#include "scope.h"
+#include "binary.h"
+
+namespace ledger {
+
+#if 0
+void expr_t::op_t::compute(value_t& result,
+ const details_t& details,
+ ptr_op_t context) const
+{
+ try {
+ switch (kind) {
+ case INDEX:
+ throw new compute_error("Cannot directly compute an argument index");
+
+ case VALUE:
+ result = as_value();
+ break;
+
+ case F_NOW:
+ result = terminus;
+ break;
+
+ case AMOUNT:
+ if (details.xact) {
+ if (transaction_has_xdata(*details.xact) &&
+ transaction_xdata_(*details.xact).dflags & TRANSACTION_COMPOUND)
+ result = transaction_xdata_(*details.xact).value;
+ else
+ result = details.xact->amount;
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value;
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case PRICE:
+ if (details.xact) {
+ bool set = false;
+ if (transaction_has_xdata(*details.xact)) {
+ transaction_xdata_t& xdata(transaction_xdata_(*details.xact));
+ if (xdata.dflags & TRANSACTION_COMPOUND) {
+ result = xdata.value.value();
+ set = true;
+ }
+ }
+ if (! set) {
+ optional<amount_t> value = details.xact->amount.value();
+ if (value)
+ result = *value;
+ else
+ result = 0L;
+ }
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value.value();
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case COST:
+ if (details.xact) {
+ bool set = false;
+ if (transaction_has_xdata(*details.xact)) {
+ transaction_xdata_t& xdata(transaction_xdata_(*details.xact));
+ if (xdata.dflags & TRANSACTION_COMPOUND) {
+ result = xdata.value.cost();
+ set = true;
+ }
+ }
+
+ if (! set) {
+ if (details.xact->cost)
+ result = *details.xact->cost;
+ else
+ result = details.xact->amount;
+ }
+ }
+ else if (details.account && account_has_xdata(*details.account)) {
+ result = account_xdata(*details.account).value.cost();
+ }
+ else {
+ result = 0L;
+ }
+ break;
+
+ case TOTAL:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = transaction_xdata_(*details.xact).total;
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total;
+ else
+ result = 0L;
+ break;
+ case PRICE_TOTAL:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = transaction_xdata_(*details.xact).total.value();
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total.value();
+ else
+ result = 0L;
+ break;
+ case COST_TOTAL:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = transaction_xdata_(*details.xact).total.cost();
+ else if (details.account && account_has_xdata(*details.account))
+ result = account_xdata(*details.account).total.cost();
+ else
+ result = 0L;
+ break;
+
+ case VALUE_EXPR:
+ if (value_expr::amount_expr.get())
+ value_expr::amount_expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+ case TOTAL_EXPR:
+ if (value_expr::total_expr.get())
+ value_expr::total_expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+
+ case DATE:
+ if (details.xact && transaction_has_xdata(*details.xact) &&
+ is_valid(transaction_xdata_(*details.xact).date))
+ result = transaction_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->date();
+ else if (details.entry)
+ result = details.entry->date();
+ else
+ result = terminus;
+ break;
+
+ case ACT_DATE:
+ if (details.xact && transaction_has_xdata(*details.xact) &&
+ is_valid(transaction_xdata_(*details.xact).date))
+ result = transaction_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->actual_date();
+ else if (details.entry)
+ result = details.entry->actual_date();
+ else
+ result = terminus;
+ break;
+
+ case EFF_DATE:
+ if (details.xact && transaction_has_xdata(*details.xact) &&
+ is_valid(transaction_xdata_(*details.xact).date))
+ result = transaction_xdata_(*details.xact).date;
+ else if (details.xact)
+ result = details.xact->effective_date();
+ else if (details.entry)
+ result = details.entry->effective_date();
+ else
+ result = terminus;
+ break;
+
+ case CLEARED:
+ if (details.xact)
+ result = details.xact->state == transaction_t::CLEARED;
+ else
+ result = false;
+ break;
+ case PENDING:
+ if (details.xact)
+ result = details.xact->state == transaction_t::PENDING;
+ else
+ result = false;
+ break;
+
+ case REAL:
+ if (details.xact)
+ result = ! (details.xact->has_flags(TRANSACTION_VIRTUAL));
+ else
+ result = true;
+ break;
+
+ case ACTUAL:
+ if (details.xact)
+ result = ! (details.xact->has_flags(TRANSACTION_AUTO));
+ else
+ result = true;
+ break;
+
+ case INDEX:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = long(transaction_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).count);
+ else
+ result = 0L;
+ break;
+
+ case COUNT:
+ if (details.xact && transaction_has_xdata(*details.xact))
+ result = long(transaction_xdata_(*details.xact).index + 1);
+ else if (details.account && account_has_xdata(*details.account))
+ result = long(account_xdata(*details.account).total_count);
+ else
+ result = 0L;
+ break;
+
+ case DEPTH:
+ if (details.account)
+ result = long(details.account->depth);
+ else
+ result = 0L;
+ break;
+
+ case F_PRICE: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.value();
+ break;
+ }
+
+ case F_DATE: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.as_datetime();
+ break;
+ }
+
+ case F_DATECMP: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result = result.as_datetime();
+ if (! result)
+ break;
+
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
+ value_t moment;
+ expr->compute(moment, details, context);
+ if (moment.is_type(value_t::DATETIME)) {
+ result.cast(value_t::INTEGER);
+ moment.cast(value_t::INTEGER);
+ result -= moment;
+ } else {
+ throw new compute_error("Invalid date passed to datecmp(value,date)",
+ new valexpr_context(expr));
+ }
+ break;
+ }
+
+ case F_YEAR:
+ case F_MONTH:
+ case F_DAY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+
+ if (! result.is_type(value_t::DATETIME))
+ throw new compute_error("Invalid date passed to year|month|day(date)",
+ new valexpr_context(expr));
+
+ const datetime_t& moment(result.as_datetime());
+ switch (kind) {
+ case F_YEAR:
+ result = (long)moment.date().year();
+ break;
+ case F_MONTH:
+ result = (long)moment.date().month();
+ break;
+ case F_DAY:
+ result = (long)moment.date().day();
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case F_ARITH_MEAN: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ if (details.xact && transaction_has_xdata(*details.xact)) {
+ expr->compute(result, details, context);
+ result /= amount_t(long(transaction_xdata_(*details.xact).index + 1));
+ }
+ else if (details.account && account_has_xdata(*details.account) &&
+ account_xdata(*details.account).total_count) {
+ expr->compute(result, details, context);
+ result /= amount_t(long(account_xdata(*details.account).total_count));
+ }
+ else {
+ result = 0L;
+ }
+ break;
+ }
+
+ case F_PARENT:
+ if (details.account && details.account->parent)
+ left()->compute(result, details_t(*details.account->parent), context);
+ break;
+
+ case F_ABS: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result.abs();
+ break;
+ }
+
+ case F_ROUND: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ result.round();
+ break;
+ }
+
+ case F_COMMODITY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+ if (! result.is_type(value_t::AMOUNT))
+ throw new compute_error("Argument to commodity() must be a commoditized amount",
+ new valexpr_context(expr));
+ amount_t temp("1");
+ temp.set_commodity(result.as_amount().commodity());
+ result = temp;
+ break;
+ }
+
+ case F_SET_COMMODITY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ value_t temp;
+ expr->compute(temp, details, context);
+
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
+ expr->compute(result, details, context);
+ if (! result.is_type(value_t::AMOUNT))
+ throw new compute_error
+ ("Second argument to set_commodity() must be a commoditized amount",
+ new valexpr_context(expr));
+ amount_t one("1");
+ one.set_commodity(result.as_amount().commodity());
+ result = one;
+
+ result *= temp;
+ break;
+ }
+
+ case F_QUANTITY: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+
+ const balance_t * bal = NULL;
+ switch (result.type()) {
+ case value_t::BALANCE_PAIR:
+ bal = &(result.as_balance_pair().quantity());
+ // fall through...
+
+ case value_t::BALANCE:
+ if (! bal)
+ bal = &result.as_balance();
+
+ if (bal->amounts.size() < 2) {
+ result.cast(value_t::AMOUNT);
+ } else {
+ value_t temp;
+ for (balance_t::amounts_map::const_iterator i = bal->amounts.begin();
+ i != bal->amounts.end();
+ i++) {
+ amount_t x = (*i).second;
+ x.clear_commodity();
+ temp += x;
+ }
+ result = temp;
+ assert(temp.is_type(value_t::AMOUNT));
+ }
+ // fall through...
+
+ case value_t::AMOUNT:
+ result.as_amount_lval().clear_commodity();
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case F_CODE_MASK:
+ if (details.entry && details.entry->code)
+ result = as_mask().match(*details.entry->code);
+ else
+ result = false;
+ break;
+
+ case F_PAYEE_MASK:
+ if (details.entry)
+ result = as_mask().match(details.entry->payee);
+ else
+ result = false;
+ break;
+
+ case F_NOTE_MASK:
+ if (details.xact && details.xact->note)
+ result = as_mask().match(*details.xact->note);
+ else
+ result = false;
+ break;
+
+ case F_ACCOUNT_MASK:
+ if (details.account)
+ result = as_mask().match(details.account->fullname());
+ else
+ result = false;
+ break;
+
+ case F_SHORT_ACCOUNT_MASK:
+ if (details.account)
+ result = as_mask().match(details.account->name);
+ else
+ result = false;
+ break;
+
+ case F_COMMODITY_MASK:
+ if (details.xact)
+ result = as_mask().match(details.xact->amount.commodity().base_symbol());
+ else
+ result = false;
+ break;
+
+ case O_ARG: {
+ long arg_index = 0;
+ assert(left()->kind == INDEX);
+ ptr_op_t expr = find_leaf(context, left()->as_long(), arg_index);
+ if (expr)
+ expr->compute(result, details, context);
+ else
+ result = 0L;
+ break;
+ }
+
+ case O_COMMA:
+ if (! left())
+ throw new compute_error("Comma operator missing left operand",
+ new valexpr_context(const_cast<op_t *>(this)));
+ if (! right())
+ throw new compute_error("Comma operator missing right operand",
+ new valexpr_context(const_cast<op_t *>(this)));
+ left()->compute(result, details, context);
+ right()->compute(result, details, context);
+ break;
+
+ case O_DEF:
+ result = 0L;
+ break;
+
+ case O_REF: {
+ assert(left());
+ if (right()) {
+ value_expr args(reduce_leaves(right(), details, context));
+ left()->compute(result, details, args.get());
+ } else {
+ left()->compute(result, details, context);
+ }
+ break;
+ }
+
+ case F_VALUE: {
+ long arg_index = 0;
+ ptr_op_t expr = find_leaf(context, 0, arg_index);
+ expr->compute(result, details, context);
+
+ arg_index = 0;
+ expr = find_leaf(context, 1, arg_index);
+ value_t moment;
+ expr->compute(moment, details, context);
+ if (! moment.is_type(value_t::DATETIME))
+ throw new compute_error("Invalid date passed to P(value,date)",
+ new valexpr_context(expr));
+
+ result = result.value(moment.as_datetime());
+ break;
+ }
+
+ case O_NOT:
+ left()->compute(result, details, context);
+ if (result.strip_annotations())
+ result = false;
+ else
+ result = true;
+ break;
+
+ case O_QUES: {
+ assert(left());
+ assert(right());
+ assert(right()->kind == O_COL);
+ left()->compute(result, details, context);
+ if (result.strip_annotations())
+ right()->left()->compute(result, details, context);
+ else
+ right()->right()->compute(result, details, context);
+ break;
+ }
+
+ case O_AND:
+ assert(left());
+ assert(right());
+ left()->compute(result, details, context);
+ result = result.strip_annotations();
+ if (result)
+ right()->compute(result, details, context);
+ break;
+
+ case O_OR:
+ assert(left());
+ assert(right());
+ left()->compute(result, details, context);
+ if (! result.strip_annotations())
+ right()->compute(result, details, context);
+ break;
+
+ case O_NEQ:
+ case O_EQ:
+ case O_LT:
+ case O_LTE:
+ case O_GT:
+ case O_GTE: {
+ assert(left());
+ assert(right());
+ value_t temp;
+ left()->compute(temp, details, context);
+ right()->compute(result, details, context);
+ switch (kind) {
+ case O_NEQ: result = temp != result; break;
+ case O_EQ: result = temp == result; break;
+ case O_LT: result = temp < result; break;
+ case O_LTE: result = temp <= result; break;
+ case O_GT: result = temp > result; break;
+ case O_GTE: result = temp >= result; break;
+ default: assert(false); break;
+ }
+ break;
+ }
+
+ case O_NEG:
+ assert(left());
+ left()->compute(result, details, context);
+ result.negate();
+ break;
+
+ case O_ADD:
+ case O_SUB:
+ case O_MUL:
+ case O_DIV: {
+ assert(left());
+ assert(right());
+ value_t temp;
+ right()->compute(temp, details, context);
+ left()->compute(result, details, context);
+ switch (kind) {
+ case O_ADD: result += temp; break;
+ case O_SUB: result -= temp; break;
+ case O_MUL: result *= temp; break;
+ case O_DIV: result /= temp; break;
+ default: assert(false); break;
+ }
+ break;
+ }
+
+ case O_PERC: {
+ assert(left());
+ result = "100.0%";
+ value_t temp;
+ left()->compute(temp, details, context);
+ result *= temp;
+ break;
+ }
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+ }
+ catch (error * err) {
+ if (err->context.empty() ||
+ ! dynamic_cast<valexpr_context *>(err->context.back()))
+ err->context.push_back(new valexpr_context(const_cast<op_t *>(this)));
+ throw err;
+ }
+}
+#endif
+
+expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope)
+{
+ switch (kind) {
+ case IDENT:
+ if (ptr_op_t def = scope.lookup(as_ident())) {
+#if 1
+ return def;
+#else
+ // Aren't definitions compiled when they go in? Would
+ // recompiling here really add any benefit?
+ return def->compile(scope);
+#endif
+ }
+ return this;
+
+ default:
+ break;
+ }
+
+ if (kind < TERMINALS)
+ return this;
+
+ ptr_op_t lhs(left()->compile(scope));
+ ptr_op_t rhs(right() ? right()->compile(scope) : ptr_op_t());
+
+ if (lhs == left() && (! rhs || rhs == right()))
+ return this;
+
+ ptr_op_t intermediate(copy(lhs, rhs));
+
+ if (lhs->is_value() && (! rhs || rhs->is_value()))
+ return wrap_value(intermediate->calc(scope));
+
+ return intermediate;
+}
+
+value_t expr_t::op_t::calc(scope_t& scope)
+{
+ switch (kind) {
+ case VALUE:
+ return as_value();
+
+ case IDENT:
+ if (ptr_op_t reference = compile(scope)) {
+ if (reference != this)
+ return reference->calc(scope);
+ }
+ throw_(calc_error, "Unknown identifier '" << as_ident() << "'");
+
+ case FUNCTION:
+ // This should never be evaluated directly; it only appears as the
+ // left node of an O_CALL operator.
+ assert(false);
+ break;
+
+ case O_CALL: {
+ call_scope_t call_args(scope);
+
+ if (right())
+ call_args.set_args(right()->calc(scope));
+
+ ptr_op_t func = left();
+ string name;
+
+ if (func->kind == IDENT) {
+ name = func->as_ident();
+ ptr_op_t def = func->compile(scope);
+ if (def == func)
+ throw_(calc_error,
+ "Attempt to call unknown function '" << name << "'");
+ func = def;
+ }
+
+ if (func->kind != FUNCTION)
+ throw_(calc_error, "Attempt to call non-function");
+
+ return func->as_function()(call_args);
+ }
+
+ case INDEX: {
+ call_scope_t args(scope);
+
+ if (as_index() >= 0 && as_index() < args.size())
+ return args[as_index()];
+ else
+ throw_(calc_error, "Reference to non-existing argument " << as_index());
+ break;
+ }
+
+ case O_NEQ:
+ return left()->calc(scope) != right()->calc(scope);
+ case O_EQ:
+ return left()->calc(scope) == right()->calc(scope);
+ case O_LT:
+ return left()->calc(scope) < right()->calc(scope);
+ case O_LTE:
+ return left()->calc(scope) <= right()->calc(scope);
+ case O_GT:
+ return left()->calc(scope) > right()->calc(scope);
+ case O_GTE:
+ return left()->calc(scope) >= right()->calc(scope);
+
+ case O_ADD:
+ return left()->calc(scope) + right()->calc(scope);
+ case O_SUB:
+ return left()->calc(scope) - right()->calc(scope);
+ case O_MUL:
+ return left()->calc(scope) * right()->calc(scope);
+ case O_DIV:
+ return left()->calc(scope) / right()->calc(scope);
+
+ case O_NEG:
+ assert(! right());
+ return left()->calc(scope).negate();
+
+ case O_NOT:
+ assert(! right());
+ return ! left()->calc(scope);
+
+ case O_AND:
+ return left()->calc(scope) && right()->calc(scope);
+ case O_OR:
+ return left()->calc(scope) || right()->calc(scope);
+
+ case O_COMMA: {
+ value_t result(left()->calc(scope));
+
+ ptr_op_t next = right();
+ while (next) {
+ ptr_op_t value_op;
+ if (next->kind == O_COMMA /* || next->kind == O_UNION */) {
+ value_op = next->left();
+ next = next->right();
+ } else {
+ value_op = next;
+ next = NULL;
+ }
+
+ result.push_back(value_op->calc(scope));
+ }
+ return result;
+ }
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ return NULL_VALUE;
+}
+
+bool expr_t::op_t::print(std::ostream& out, print_context_t& context) const
+{
+ bool found = false;
+
+ if (context.start_pos && this == context.op_to_find) {
+ *context.start_pos = (long)out.tellp() - 1;
+ found = true;
+ }
+
+ string symbol;
+
+ switch (kind) {
+ case VALUE: {
+ as_value().print(out, context.relaxed);
+ break;
+ }
+
+ case IDENT:
+ out << as_ident();
+ break;
+
+ case FUNCTION:
+ out << "<FUNCTION>";
+ break;
+
+ case INDEX:
+ out << '@' << as_index();
+ break;
+
+ case O_NOT:
+ out << "!";
+ if (left() && left()->print(out, context))
+ found = true;
+ break;
+ case O_NEG:
+ out << "-";
+ if (left() && left()->print(out, context))
+ found = true;
+ break;
+
+ case O_ADD:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " + ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_SUB:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " - ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_MUL:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " * ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_DIV:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " / ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_NEQ:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " != ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_EQ:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " == ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_LT:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " < ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_LTE:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " <= ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_GT:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " > ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_GTE:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " >= ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_AND:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " & ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+ case O_OR:
+ out << "(";
+ if (left() && left()->print(out, context))
+ found = true;
+ out << " | ";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case O_COMMA:
+ if (left() && left()->print(out, context))
+ found = true;
+ out << ", ";
+ if (right() && right()->print(out, context))
+ found = true;
+ break;
+
+ case O_CALL:
+ if (left() && left()->print(out, context))
+ found = true;
+ out << "(";
+ if (right() && right()->print(out, context))
+ found = true;
+ out << ")";
+ break;
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ if (! symbol.empty()) {
+ if (amount_t::current_pool->find(symbol))
+ out << '@';
+ out << symbol;
+ }
+
+ if (context.end_pos && this == context.op_to_find)
+ *context.end_pos = (long)out.tellp() - 1;
+
+ return found;
+}
+
+void expr_t::op_t::dump(std::ostream& out, const int depth) const
+{
+ out.setf(std::ios::left);
+ out.width(10);
+ out << this << " ";
+
+ for (int i = 0; i < depth; i++)
+ out << " ";
+
+ switch (kind) {
+ case VALUE:
+ out << "VALUE - " << as_value();
+ break;
+
+ case IDENT:
+ out << "IDENT - " << as_ident();
+ break;
+
+ case INDEX:
+ out << "INDEX - " << as_index();
+ break;
+
+ case FUNCTION:
+ out << "FUNCTION";
+ break;
+
+ case O_CALL: out << "O_CALL"; break;
+
+ case O_NOT: out << "O_NOT"; break;
+ case O_NEG: out << "O_NEG"; break;
+
+ case O_ADD: out << "O_ADD"; break;
+ case O_SUB: out << "O_SUB"; break;
+ case O_MUL: out << "O_MUL"; break;
+ case O_DIV: out << "O_DIV"; break;
+
+ case O_NEQ: out << "O_NEQ"; break;
+ case O_EQ: out << "O_EQ"; break;
+ case O_LT: out << "O_LT"; break;
+ case O_LTE: out << "O_LTE"; break;
+ case O_GT: out << "O_GT"; break;
+ case O_GTE: out << "O_GTE"; break;
+
+ case O_AND: out << "O_AND"; break;
+ case O_OR: out << "O_OR"; break;
+
+ case O_COMMA: out << "O_COMMA"; break;
+
+ case LAST:
+ default:
+ assert(false);
+ break;
+ }
+
+ out << " (" << refc << ')' << std::endl;
+
+ if (kind > TERMINALS) {
+ if (left()) {
+ left()->dump(out, depth + 1);
+ if (right())
+ right()->dump(out, depth + 1);
+ } else {
+ assert(! right());
+ }
+ }
+}
+
+void expr_t::op_t::read(std::ostream& in)
+{
+}
+
+void expr_t::op_t::read(const char *& data)
+{
+#if 0
+ if (! read_bool(data))
+ return expr_t::ptr_op_t();
+
+ expr_t::op_t::kind_t kind;
+ read_number(data, kind);
+
+ expr_t::ptr_op_t expr = new expr_t::op_t(kind);
+
+ if (kind > expr_t::op_t::TERMINALS)
+ expr->set_left(read_value_expr(data));
+
+ switch (expr->kind) {
+ case expr_t::op_t::INDEX: {
+ long temp;
+ read_long(data, temp);
+ expr->set_index(temp);
+ break;
+ }
+ case expr_t::op_t::VALUE: {
+ value_t temp;
+ read_value(data, temp);
+ expr->set_value(temp);
+ break;
+ }
+
+ case expr_t::op_t::MASK:
+ if (read_bool(data))
+ read_mask(data, expr->as_mask_lval());
+ break;
+
+ default:
+ if (kind > expr_t::op_t::TERMINALS)
+ expr->set_right(read_value_expr(data));
+ break;
+ }
+
+ return expr;
+#endif
+}
+
+void expr_t::op_t::write(std::ostream& out) const
+{
+#if 0
+ if (! expr) {
+ write_bool(out, false);
+ return;
+ }
+
+ write_bool(out, true);
+ write_number(out, expr->kind);
+
+ if (expr->kind > expr_t::op_t::TERMINALS)
+ write_value_expr(out, expr->left());
+
+ switch (expr->kind) {
+ case expr_t::op_t::INDEX:
+ write_long(out, expr->as_index());
+ break;
+ case expr_t::op_t::IDENT:
+ write_long(out, expr->as_ident());
+ break;
+ case expr_t::op_t::VALUE:
+ write_value(out, expr->as_value());
+ break;
+
+ case expr_t::op_t::MASK:
+ if (expr->as_mask()) {
+ write_bool(out, true);
+ write_mask(out, expr->as_mask());
+ } else {
+ write_bool(out, false);
+ }
+ break;
+
+ default:
+ if (expr->kind > expr_t::op_t::TERMINALS)
+ write_value_expr(out, expr->right());
+ break;
+ }
+#endif
+}
+
+#if 0
+class op_predicate : public noncopyable
+{
+ ptr_op_t op;
+
+ op_predicate();
+
+public:
+ explicit op_predicate(ptr_op_t _op) : op(_op) {
+ TRACE_CTOR(op_predicate, "ptr_op_t");
+ }
+ ~op_predicate() throw() {
+ TRACE_DTOR(op_predicate);
+ }
+ bool operator()(scope_t& scope) {
+ return op->calc(scope).to_boolean();
+ }
+};
+
+#endif
+
+} // namespace ledger
diff --git a/op.h b/op.h
new file mode 100644
index 00000000..ec5273b0
--- /dev/null
+++ b/op.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OP_H
+#define _OP_H
+
+#include "expr.h"
+#include "mask.h"
+
+namespace ledger {
+
+class expr_t::op_t : public noncopyable
+{
+ friend class expr_t;
+ friend class expr_t::parser_t;
+
+ op_t();
+
+public:
+ typedef expr_t::ptr_op_t ptr_op_t;
+
+private:
+ mutable short refc;
+ ptr_op_t left_;
+
+ variant<unsigned int, // used by constant INDEX
+ value_t, // used by constant VALUE
+ string, // used by constant IDENT
+ mask_t, // used by constant MASK
+ function_t, // used by terminal FUNCTION
+ ptr_op_t> // used by all binary operators
+ data;
+
+public:
+ enum kind_t {
+ // Constants
+ VALUE,
+ IDENT,
+ MASK,
+ INDEX,
+
+ CONSTANTS,
+
+ FUNCTION,
+
+ TERMINALS,
+
+ // Binary operators
+ O_NOT,
+ O_EQ,
+ O_NEQ,
+ O_LT,
+ O_LTE,
+ O_GT,
+ O_GTE,
+
+ O_AND,
+ O_OR,
+
+ O_NEG,
+ O_ADD,
+ O_SUB,
+ O_MUL,
+ O_DIV,
+
+ O_QUERY,
+ O_COLON,
+
+ O_COMMA,
+
+ O_CALL,
+
+ OPERATORS,
+
+ LAST
+ };
+
+ kind_t kind;
+
+ explicit op_t(const kind_t _kind) : refc(0), kind(_kind) {
+ TRACE_CTOR(op_t, "const kind_t");
+ }
+ ~op_t() {
+ TRACE_DTOR(op_t);
+ assert(refc == 0);
+ }
+
+ bool is_index() const {
+ return data.type() == typeid(unsigned int);
+ }
+ unsigned int& as_index_lval() {
+ assert(kind == INDEX);
+ return boost::get<unsigned int>(data);
+ }
+ const unsigned int& as_index() const {
+ return const_cast<op_t *>(this)->as_index_lval();
+ }
+ void set_index(unsigned int val) {
+ data = val;
+ }
+
+ bool is_value() const {
+ if (kind == VALUE) {
+ assert(data.type() == typeid(value_t));
+ return true;
+ }
+ return false;
+ }
+ value_t& as_value_lval() {
+ assert(is_value());
+ value_t& val(boost::get<value_t>(data));
+ assert(val.valid());
+ return val;
+ }
+ const value_t& as_value() const {
+ return const_cast<op_t *>(this)->as_value_lval();
+ }
+ void set_value(const value_t& val) {
+ assert(val.valid());
+ data = val;
+ }
+
+ bool is_ident() const {
+ if (kind == IDENT) {
+ assert(data.type() == typeid(string));
+ return true;
+ }
+ return false;
+ }
+ string& as_ident_lval() {
+ assert(is_ident());
+ return boost::get<string>(data);
+ }
+ const string& as_ident() const {
+ return const_cast<op_t *>(this)->as_ident_lval();
+ }
+ void set_ident(const string& val) {
+ data = val;
+ }
+
+ bool is_mask() const {
+ if (kind == MASK) {
+ assert(data.type() == typeid(mask_t));
+ return true;
+ }
+ return false;
+ }
+ mask_t& as_mask_lval() {
+ assert(is_mask());
+ return boost::get<mask_t>(data);
+ }
+ const mask_t& as_mask() const {
+ return const_cast<op_t *>(this)->as_mask_lval();
+ }
+ void set_mask(const mask_t& val) {
+ data = val;
+ }
+ void set_mask(const string& expr) {
+ data = mask_t(expr);
+ }
+
+ bool is_function() const {
+ return kind == FUNCTION;
+ }
+ function_t& as_function_lval() {
+ assert(kind == FUNCTION);
+ return boost::get<function_t>(data);
+ }
+ const function_t& as_function() const {
+ return const_cast<op_t *>(this)->as_function_lval();
+ }
+ void set_function(const function_t& val) {
+ data = val;
+ }
+
+ ptr_op_t& left() {
+ return left_;
+ }
+ const ptr_op_t& left() const {
+ assert(kind > TERMINALS);
+ return left_;
+ }
+ void set_left(const ptr_op_t& expr) {
+ assert(kind > TERMINALS);
+ left_ = expr;
+ }
+
+ ptr_op_t& as_op_lval() {
+ assert(kind > TERMINALS);
+ return boost::get<ptr_op_t>(data);
+ }
+ const ptr_op_t& as_op() const {
+ return const_cast<op_t *>(this)->as_op_lval();
+ }
+
+ ptr_op_t& right() {
+ assert(kind > TERMINALS);
+ return as_op_lval();
+ }
+ const ptr_op_t& right() const {
+ assert(kind > TERMINALS);
+ return as_op();
+ }
+ void set_right(const ptr_op_t& expr) {
+ assert(kind > TERMINALS);
+ data = expr;
+ }
+
+private:
+ void acquire() const {
+ DEBUG("ledger.xpath.memory",
+ "Acquiring " << this << ", refc now " << refc + 1);
+ assert(refc >= 0);
+ refc++;
+ }
+ void release() const {
+ DEBUG("ledger.xpath.memory",
+ "Releasing " << this << ", refc now " << refc - 1);
+ assert(refc > 0);
+ if (--refc == 0)
+ checked_delete(this);
+ }
+
+ friend inline void intrusive_ptr_add_ref(op_t * op) {
+ op->acquire();
+ }
+ friend inline void intrusive_ptr_release(op_t * op) {
+ op->release();
+ }
+
+ static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
+ ptr_op_t _right = NULL);
+
+ ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
+ return new_node(kind, _left, _right);
+ }
+
+public:
+ ptr_op_t compile(scope_t& scope);
+ value_t calc(scope_t& scope);
+
+ struct print_context_t
+ {
+ scope_t& scope;
+ const bool relaxed;
+ const ptr_op_t& op_to_find;
+ unsigned long * start_pos;
+ unsigned long * end_pos;
+
+ print_context_t(scope_t& _scope,
+ const bool _relaxed = false,
+ const ptr_op_t& _op_to_find = ptr_op_t(),
+ unsigned long * _start_pos = NULL,
+ unsigned long * _end_pos = NULL)
+ : scope(_scope), relaxed(_relaxed), op_to_find(_op_to_find),
+ start_pos(_start_pos), end_pos(_end_pos) {}
+ };
+
+ bool print(std::ostream& out, print_context_t& context) const;
+ void dump(std::ostream& out, const int depth) const;
+
+ void read(std::ostream& in);
+ void read(const char *& data);
+ void write(std::ostream& out) const;
+
+ static ptr_op_t wrap_value(const value_t& val);
+ static ptr_op_t wrap_functor(const function_t& fobj);
+};
+
+inline expr_t::ptr_op_t
+expr_t::op_t::new_node(kind_t _kind, ptr_op_t _left, ptr_op_t _right)
+{
+ ptr_op_t node(new op_t(_kind));
+ node->set_left(_left);
+ node->set_right(_right);
+ return node;
+}
+
+inline expr_t::ptr_op_t expr_t::op_t::wrap_value(const value_t& val) {
+ ptr_op_t temp(new op_t(op_t::VALUE));
+ temp->set_value(val);
+ return temp;
+}
+
+inline expr_t::ptr_op_t expr_t::op_t::wrap_functor(const function_t& fobj) {
+ ptr_op_t temp(new op_t(op_t::FUNCTION));
+ temp->set_function(fobj);
+ return temp;
+}
+
+#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)
+
+} // namespace ledger
+
+#endif // _OP_H
diff --git a/option.cc b/option.cc
index 103e960e..2edebbef 100644
--- a/option.cc
+++ b/option.cc
@@ -34,9 +34,9 @@
namespace ledger {
namespace {
- typedef tuple<expr::ptr_op_t, bool> op_bool_tuple;
+ typedef tuple<expr_t::ptr_op_t, bool> op_bool_tuple;
- op_bool_tuple find_option(expr::scope_t& scope, const string& name)
+ op_bool_tuple find_option(scope_t& scope, const string& name)
{
char buf[128];
std::strcpy(buf, "option_");
@@ -49,7 +49,7 @@ namespace {
}
*p = '\0';
- expr::ptr_op_t op = scope.lookup(buf);
+ expr_t::ptr_op_t op = scope.lookup(buf);
if (op)
return op_bool_tuple(op, false);
@@ -59,14 +59,14 @@ namespace {
return op_bool_tuple(scope.lookup(buf), true);
}
- op_bool_tuple find_option(expr::scope_t& scope, const char letter)
+ op_bool_tuple find_option(scope_t& scope, const char letter)
{
char buf[10];
std::strcpy(buf, "option_");
buf[7] = letter;
buf[8] = '\0';
- expr::ptr_op_t op = scope.lookup(buf);
+ expr_t::ptr_op_t op = scope.lookup(buf);
if (op)
return op_bool_tuple(op, false);
@@ -76,13 +76,13 @@ namespace {
return op_bool_tuple(scope.lookup(buf), true);
}
- void process_option(const expr::function_t& opt,
- expr::scope_t& scope, const char * arg)
+ void process_option(const function_t& opt, scope_t& scope,
+ const char * arg)
{
#if 0
try {
#endif
- expr::call_scope_t args(scope);
+ call_scope_t args(scope);
if (arg)
args.push_back(value_t(arg, true));
@@ -101,7 +101,7 @@ namespace {
}
}
-void process_option(const string& name, expr::scope_t& scope,
+void process_option(const string& name, scope_t& scope,
const char * arg)
{
op_bool_tuple opt(find_option(scope, name));
@@ -110,7 +110,7 @@ void process_option(const string& name, expr::scope_t& scope,
}
void process_environment(const char ** envp, const string& tag,
- expr::scope_t& scope)
+ scope_t& scope)
{
const char * tag_p = tag.c_str();
unsigned int tag_len = tag.length();
@@ -149,7 +149,7 @@ void process_environment(const char ** envp, const string& tag,
}
void process_arguments(int argc, char ** argv, const bool anywhere,
- expr::scope_t& scope, std::list<string>& args)
+ scope_t& scope, std::list<string>& args)
{
for (char ** i = argv; *i; i++) {
if ((*i)[0] != '-') {
@@ -190,7 +190,7 @@ void process_arguments(int argc, char ** argv, const bool anywhere,
throw_(option_error, "illegal option -");
}
else {
- typedef tuple<expr::ptr_op_t, bool, char> op_bool_char_tuple;
+ typedef tuple<expr_t::ptr_op_t, bool, char> op_bool_char_tuple;
std::list<op_bool_char_tuple> option_queue;
diff --git a/option.h b/option.h
index 10ed3f4d..fa908eff 100644
--- a/option.h
+++ b/option.h
@@ -32,19 +32,18 @@
#ifndef _OPTION_H
#define _OPTION_H
-#include "valexpr.h"
+#include "scope.h"
namespace ledger {
-void process_option(const string& name, expr::scope_t& scope,
+void process_option(const string& name, scope_t& scope,
const char * arg = NULL);
void process_environment(const char ** envp, const string& tag,
- expr::scope_t& scope);
+ scope_t& scope);
void process_arguments(int argc, char ** argv, const bool anywhere,
- expr::scope_t& scope,
- std::list<string>& args);
+ scope_t& scope, std::list<string>& args);
DECLARE_EXCEPTION(error, option_error);
diff --git a/parser.cc b/parser.cc
new file mode 100644
index 00000000..a03ea9df
--- /dev/null
+++ b/parser.cc
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#include "parser.h"
+
+namespace ledger {
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_value_term(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node;
+
+ token_t& tok = next_token(in, tflags);
+
+ switch (tok.kind) {
+ case token_t::VALUE:
+ node = new op_t(op_t::VALUE);
+ node->set_value(tok.value);
+ break;
+
+ case token_t::MASK: {
+ // A /mask/ is just a shorthand for calling match().
+ node = new op_t(op_t::O_CALL);
+
+ ptr_op_t ident = new op_t(op_t::IDENT);
+ ident->set_ident("match");
+ node->set_left(ident);
+
+ ptr_op_t args = new op_t(op_t::O_COMMA);
+ node->set_right(args);
+
+ ptr_op_t mask = new op_t(op_t::MASK);
+ mask->set_mask(tok.value.as_string());
+
+ ident = new op_t(op_t::IDENT);
+
+ args->set_left(mask);
+ args->set_right(ident);
+
+ switch (tok.flags()) {
+ case TOKEN_SHORT_ACCOUNT_MASK:
+ ident->set_ident("account_base");
+ break;
+ case TOKEN_CODE_MASK:
+ ident->set_ident("code");
+ break;
+#if 0
+ case TOKEN_COMMODITY_MASK:
+ ident->set_ident("commodity");
+ break;
+#endif
+ case TOKEN_PAYEE_MASK:
+ ident->set_ident("payee");
+ break;
+ case TOKEN_NOTE_MASK:
+ ident->set_ident("note");
+ break;
+ case TOKEN_ACCOUNT_MASK:
+ ident->set_ident("account");
+ break;
+ }
+ break;
+ }
+
+ case token_t::IDENT: {
+#if 0
+#ifdef USE_BOOST_PYTHON
+ if (tok.value->as_string() == "lambda") // special
+ try {
+ char c, buf[4096];
+
+ std::strcpy(buf, "lambda ");
+ READ_INTO(in, &buf[7], 4000, c, true);
+
+ ptr_op_t eval = new op_t(op_t::O_EVAL);
+ ptr_op_t lambda = new op_t(op_t::FUNCTION);
+ lambda->functor = new python_functor_t(python_eval(buf));
+ eval->set_left(lambda);
+ ptr_op_t sym = new op_t(op_t::SYMBOL);
+ sym->name = new string("__ptr");
+ eval->set_right(sym);
+
+ node = eval;
+
+ goto done;
+ }
+ catch(const boost::python::error_already_set&) {
+ throw_(parse_error, "Error parsing lambda expression");
+ }
+#endif /* USE_BOOST_PYTHON */
+#endif
+
+ string ident = tok.value.as_string();
+
+ // An identifier followed by ( represents a function call
+ tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::LPAREN) {
+ node = new op_t(op_t::IDENT);
+ node->set_ident(ident);
+
+ ptr_op_t call_node(new op_t(op_t::O_CALL));
+ call_node->set_left(node);
+ call_node->set_right(parse_value_expr(in, tflags | EXPR_PARSE_PARTIAL));
+
+ tok = next_token(in, tflags);
+ if (tok.kind != token_t::RPAREN)
+ tok.unexpected(0xff, ')');
+
+ node = call_node;
+ } else {
+ if (std::isdigit(ident[0])) {
+ node = new op_t(op_t::INDEX);
+ node->set_index(lexical_cast<unsigned int>(ident.c_str()));
+ } else {
+ node = new op_t(op_t::IDENT);
+ node->set_ident(ident);
+ }
+ push_token(tok);
+ }
+ break;
+ }
+
+ case token_t::LPAREN:
+ node = new op_t(op_t::O_COMMA);
+ node->set_left(parse_value_expr(in, tflags | EXPR_PARSE_PARTIAL));
+ if (! node->left())
+ throw_(parse_error, tok.symbol << " operator not followed by argument");
+
+ tok = next_token(in, tflags);
+ if (tok.kind != token_t::RPAREN)
+ tok.unexpected(0xff, ')');
+ break;
+
+ default:
+ push_token(tok);
+ break;
+ }
+
+#if 0
+#ifdef USE_BOOST_PYTHON
+ done:
+#endif
+#endif
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_unary_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node;
+
+ token_t& tok = next_token(in, tflags);
+
+ switch (tok.kind) {
+ case token_t::EXCLAM: {
+ ptr_op_t term(parse_value_term(in, tflags));
+ if (! term)
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+
+ // A very quick optimization
+ if (term->kind == op_t::VALUE) {
+ term->as_value_lval().in_place_negate();
+ node = term;
+ } else {
+ node = new op_t(op_t::O_NOT);
+ node->set_left(term);
+ }
+ break;
+ }
+
+ case token_t::MINUS: {
+ ptr_op_t term(parse_value_term(in, tflags));
+ if (! term)
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+
+ // A very quick optimization
+ if (term->kind == op_t::VALUE) {
+ term->as_value_lval().in_place_negate();
+ node = term;
+ } else {
+ node = new op_t(op_t::O_NEG);
+ node->set_left(term);
+ }
+ break;
+ }
+
+ default:
+ push_token(tok);
+ node = parse_value_term(in, tflags);
+ break;
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_mul_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_unary_expr(in, tflags));
+
+ if (node) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::STAR || tok.kind == token_t::KW_DIV) {
+ ptr_op_t prev(node);
+ node = new op_t(tok.kind == token_t::STAR ?
+ op_t::O_MUL : op_t::O_DIV);
+ node->set_left(prev);
+ node->set_right(parse_mul_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+
+ tok = next_token(in, tflags);
+ }
+ push_token(tok);
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_add_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_mul_expr(in, tflags));
+
+ if (node) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::PLUS ||
+ tok.kind == token_t::MINUS) {
+ ptr_op_t prev(node);
+ node = new op_t(tok.kind == token_t::PLUS ?
+ op_t::O_ADD : op_t::O_SUB);
+ node->set_left(prev);
+ node->set_right(parse_add_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+
+ tok = next_token(in, tflags);
+ }
+ push_token(tok);
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_logic_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_add_expr(in, tflags));
+
+ if (node) {
+ op_t::kind_t kind = op_t::LAST;
+ flags_t _flags = tflags;
+ token_t& tok = next_token(in, tflags);
+
+ switch (tok.kind) {
+ case token_t::EQUAL:
+ if (tflags & EXPR_PARSE_NO_ASSIGN)
+ tok.rewind(in);
+ else
+ kind = op_t::O_EQ;
+ break;
+ case token_t::NEQUAL:
+ kind = op_t::O_NEQ;
+ break;
+ case token_t::LESS:
+ kind = op_t::O_LT;
+ break;
+ case token_t::LESSEQ:
+ kind = op_t::O_LTE;
+ break;
+ case token_t::GREATER:
+ kind = op_t::O_GT;
+ break;
+ case token_t::GREATEREQ:
+ kind = op_t::O_GTE;
+ break;
+ default:
+ push_token(tok);
+ break;
+ }
+
+ if (kind != op_t::LAST) {
+ ptr_op_t prev(node);
+ node = new op_t(kind);
+ node->set_left(prev);
+ node->set_right(parse_add_expr(in, _flags));
+
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+ }
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_and_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_logic_expr(in, tflags));
+
+ if (node) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::KW_AND) {
+ ptr_op_t prev(node);
+ node = new op_t(op_t::O_AND);
+ node->set_left(prev);
+ node->set_right(parse_and_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+ } else {
+ push_token(tok);
+ }
+ }
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_or_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_and_expr(in, tflags));
+
+ if (node) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::KW_OR) {
+ ptr_op_t prev(node);
+ node = new op_t(op_t::O_OR);
+ node->set_left(prev);
+ node->set_right(parse_or_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+ } else {
+ push_token(tok);
+ }
+ }
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse_value_expr(std::istream& in,
+ const flags_t tflags) const
+{
+ ptr_op_t node(parse_or_expr(in, tflags));
+
+ if (node) {
+ token_t& tok = next_token(in, tflags);
+
+ if (tok.kind == token_t::COMMA) {
+ ptr_op_t prev(node);
+ node = new op_t(op_t::O_COMMA);
+ node->set_left(prev);
+ node->set_right(parse_value_expr(in, tflags));
+ if (! node->right())
+ throw_(parse_error,
+ tok.symbol << " operator not followed by argument");
+ tok = next_token(in, tflags);
+ }
+
+ if (tok.kind != token_t::TOK_EOF) {
+ if (tflags & EXPR_PARSE_PARTIAL)
+ push_token(tok);
+ else
+ tok.unexpected();
+ }
+ }
+ else if (! (tflags & EXPR_PARSE_PARTIAL)) {
+ throw_(parse_error, "Failed to parse value expression");
+ }
+
+ return node;
+}
+
+expr_t::ptr_op_t
+expr_t::parser_t::parse(std::istream& in, const flags_t flags)
+{
+#if 0
+ try {
+#endif
+ ptr_op_t top_node = parse_value_expr(in, flags);
+
+ if (use_lookahead) {
+ use_lookahead = false;
+ lookahead.rewind(in);
+ }
+ lookahead.clear();
+
+ return top_node;
+#if 0
+ }
+ catch (error * err) {
+ err->context.push_back
+ (new line_context(str, (long)in.tellg() - 1,
+ "While parsing value expression:"));
+ throw err;
+ }
+#endif
+}
+
+} // namespace ledger
diff --git a/parser.h b/parser.h
index 3815780c..6b68c41b 100644
--- a/parser.h
+++ b/parser.h
@@ -1,137 +1,102 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
#ifndef _PARSER_H
#define _PARSER_H
-#include "utils.h"
+#include "token.h"
+#include "op.h"
namespace ledger {
-class account_t;
-class journal_t;
-class session_t;
-
-class parser_t : public noncopyable
+class expr_t::parser_t : public noncopyable
{
-public:
- parser_t() {
- TRACE_CTOR(parser_t, "");
- }
- virtual ~parser_t() {
- TRACE_DTOR(parser_t);
+#define EXPR_PARSE_NORMAL 0x00
+#define EXPR_PARSE_PARTIAL 0x01
+#define EXPR_PARSE_NO_MIGRATE 0x02
+#define EXPR_PARSE_NO_REDUCE 0x04
+#define EXPR_PARSE_NO_ASSIGN 0x08
+#define EXPR_PARSE_NO_DATES 0x10
+
+ mutable token_t lookahead;
+ mutable bool use_lookahead;
+
+ token_t& next_token(std::istream& in, token_t::flags_t tflags) const
+ {
+ if (use_lookahead)
+ use_lookahead = false;
+ else
+ lookahead.next(in, tflags);
+ return lookahead;
}
- virtual bool test(std::istream& in) const = 0;
-
- virtual unsigned int parse(std::istream& in,
- session_t& session,
- journal_t& journal,
- account_t * master = NULL,
- const path * original_file = NULL) = 0;
-};
-
-unsigned int parse_journal(std::istream& in,
- session_t& session,
- journal_t& journal,
- account_t * master = NULL,
- const path * original_file = NULL);
-
-unsigned int parse_journal_file(const path& path,
- session_t& session,
- journal_t& journal,
- account_t * master = NULL,
- const path * original_file = NULL);
+ void push_token(const token_t& tok) const
+ {
+ assert(&tok == &lookahead);
+ use_lookahead = true;
+ }
-unsigned int parse_ledger_data(session_t& session,
- journal_t& journal,
- parser_t * cache_parser = NULL,
- parser_t * xml_parser = NULL,
- parser_t * stdin_parser = NULL);
+ void push_token() const
+ {
+ use_lookahead = true;
+ }
-class parse_error : public error
-{
public:
- parse_error(const string& reason, error_context * ctxt = NULL) throw()
- : error(reason, ctxt) {}
- virtual ~parse_error() throw() {}
-};
-
-/************************************************************************
- *
- * General utility parsing functions
- */
-
-inline char * skip_ws(char * ptr) {
- while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
- ptr++;
- return ptr;
-}
+ typedef uint_least8_t flags_t;
+
+private:
+ ptr_op_t parse_value_term(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_unary_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_mul_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_add_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_logic_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_and_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_or_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_querycolon_expr(std::istream& in, const flags_t flags) const;
+ ptr_op_t parse_value_expr(std::istream& in, const flags_t flags) const;
-inline char * next_element(char * buf, bool variable = false) {
- for (char * p = buf; *p; p++) {
- if (! (*p == ' ' || *p == '\t'))
- continue;
-
- if (! variable) {
- *p = '\0';
- return skip_ws(p + 1);
- }
- else if (*p == '\t') {
- *p = '\0';
- return skip_ws(p + 1);
- }
- else if (*(p + 1) == ' ') {
- *p = '\0';
- return skip_ws(p + 2);
- }
+public:
+ parser_t() : use_lookahead(false) {
+ TRACE_CTOR(parser_t, "");
}
- return NULL;
-}
-
-inline char peek_next_nonws(std::istream& in) {
- char c = in.peek();
- while (! in.eof() && std::isspace(c)) {
- in.get(c);
- c = in.peek();
+ ~parser_t() throw() {
+ TRACE_DTOR(parser_t);
}
- return c;
-}
-
-#define READ_INTO(str, targ, size, var, cond) { \
- char * _p = targ; \
- var = str.peek(); \
- while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
- str.get(var); \
- if (str.eof()) \
- break; \
- if (var == '\\') { \
- str.get(var); \
- if (in.eof()) \
- break; \
- } \
- *_p++ = var; \
- var = str.peek(); \
- } \
- *_p = '\0'; \
-}
-#define READ_INTO_(str, targ, size, var, idx, cond) { \
- char * _p = targ; \
- var = str.peek(); \
- while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
- str.get(var); \
- if (str.eof()) \
- break; \
- idx++; \
- if (var == '\\') { \
- str.get(var); \
- if (in.eof()) \
- break; \
- idx++; \
- } \
- *_p++ = var; \
- var = str.peek(); \
- } \
- *_p = '\0'; \
-}
+ ptr_op_t parse(std::istream& in, const flags_t flags = EXPR_PARSE_NORMAL);
+ ptr_op_t parse(string& str, const flags_t flags = EXPR_PARSE_NORMAL) {
+ std::istringstream stream(str);
+ return parse(stream, flags);
+ }
+};
} // namespace ledger
diff --git a/predicate.h b/predicate.h
new file mode 100644
index 00000000..1913e1d7
--- /dev/null
+++ b/predicate.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PREDICATE_H
+#define _PREDICATE_H
+
+#include "expr.h"
+#include "scope.h"
+
+namespace ledger {
+
+template <typename T>
+class item_predicate
+{
+public:
+ expr_t predicate;
+
+ item_predicate() {
+ TRACE_CTOR(item_predicate, "");
+ }
+ item_predicate(const item_predicate& other) : predicate(other.predicate) {
+ TRACE_CTOR(item_predicate, "copy");
+ }
+ item_predicate(const expr_t& _predicate) : predicate(_predicate) {
+ TRACE_CTOR(item_predicate, "const expr_t&");
+ }
+ item_predicate(const string& _predicate) : predicate(expr_t(_predicate)) {
+ TRACE_CTOR(item_predicate, "const string&");
+ }
+ ~item_predicate() throw() {
+ TRACE_DTOR(item_predicate);
+ }
+
+ bool operator()(const T& item) const {
+#if 0
+ template context_t<T> context(item);
+ return ! predicate || predicate->calc(context).strip_annotations();
+#else
+ return false;
+#endif
+ }
+};
+
+} // namespace ledger
+
+#endif // _PREDICATE_H
diff --git a/pyinterp.cc b/pyinterp.cc
index 85d2a422..d702f291 100644
--- a/pyinterp.cc
+++ b/pyinterp.cc
@@ -86,12 +86,12 @@ struct python_run
}
};
-python_interpreter_t::python_interpreter_t(expr::scope_t& parent)
- : expr::symbol_scope_t(parent),
+python_interpreter_t::python_interpreter_t(expr_t::scope_t& parent)
+ : expr_t::symbol_scope_t(parent),
mmodule(borrowed(PyImport_AddModule("__main__"))),
nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get()))))
{
- TRACE_CTOR(python_interpreter_t, "expr::scope_t&");
+ TRACE_CTOR(python_interpreter_t, "expr_t::scope_t&");
Py_Initialize();
boost::python::detail::init_module("ledger", &initialize_for_python);
@@ -179,7 +179,7 @@ object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
}
value_t python_interpreter_t::functor_t::operator()
- (expr::call_scope_t& args)
+ (expr_t::call_scope_t& args)
{
try {
if (! PyCallable_Check(func.ptr())) {
@@ -202,7 +202,7 @@ value_t python_interpreter_t::functor_t::operator()
}
else if (PyObject * err = PyErr_Occurred()) {
PyErr_Print();
- throw_(expr::calc_error,
+ throw_(expr_t::calc_error,
"While calling Python function '" /*<< name() <<*/ "': " << err);
} else {
assert(false);
@@ -214,14 +214,14 @@ value_t python_interpreter_t::functor_t::operator()
}
catch (const error_already_set&) {
PyErr_Print();
- throw_(expr::calc_error,
+ throw_(expr_t::calc_error,
"While calling Python function '" /*<< name() <<*/ "'");
}
return NULL_VALUE;
}
value_t python_interpreter_t::lambda_t::operator()
- (expr::call_scope_t& args)
+ (expr_t::call_scope_t& args)
{
try {
assert(args.size() == 1);
@@ -231,7 +231,7 @@ value_t python_interpreter_t::lambda_t::operator()
}
catch (const error_already_set&) {
PyErr_Print();
- throw_(expr::calc_error,
+ throw_(expr_t::calc_error,
"While evaluating Python lambda expression");
}
return NULL_VALUE;
diff --git a/pyinterp.h b/pyinterp.h
index dbc3f754..4d3ac0c8 100644
--- a/pyinterp.h
+++ b/pyinterp.h
@@ -32,7 +32,7 @@
#ifndef _PYINTERP_H
#define _PYINTERP_H
-#include "valexpr.h"
+#include "scope.h"
#include <boost/python.hpp>
#include <Python.h>
@@ -40,7 +40,7 @@
namespace ledger {
class python_interpreter_t
- : public noncopyable, public expr::symbol_scope_t
+ : public noncopyable, public expr_t::symbol_scope_t
{
boost::python::handle<> mmodule;
@@ -49,7 +49,7 @@ class python_interpreter_t
public:
boost::python::dict nspace;
- python_interpreter_t(expr::scope_t& parent);
+ python_interpreter_t(expr_t::scope_t& parent);
virtual ~python_interpreter_t() {
TRACE_DTOR(python_interpreter_t);
@@ -89,14 +89,14 @@ public:
virtual ~functor_t() throw() {
TRACE_DTOR(functor_t);
}
- virtual value_t operator()(expr::call_scope_t& args);
+ virtual value_t operator()(expr_t::call_scope_t& args);
};
- virtual expr::ptr_op_t lookup(const string& name) {
+ virtual expr_t::ptr_op_t lookup(const string& name) {
if (boost::python::object func = eval(name))
return WRAP_FUNCTOR(functor_t(name, func));
else
- return expr::symbol_scope_t::lookup(name);
+ return expr_t::symbol_scope_t::lookup(name);
}
class lambda_t : public functor_t {
@@ -111,7 +111,7 @@ public:
virtual ~lambda_t() throw() {
TRACE_DTOR(lambda_t);
}
- virtual value_t operator()(expr::call_scope_t& args);
+ virtual value_t operator()(expr_t::call_scope_t& args);
};
};
diff --git a/qif.h b/qif.h
index 2e30123a..b22dcbcd 100644
--- a/qif.h
+++ b/qif.h
@@ -1,11 +1,11 @@
#ifndef _QIF_H
#define _QIF_H
-#include "parser.h"
+#include "journal.h"
namespace ledger {
-class qif_parser_t : public parser_t
+class qif_parser_t : public journal_t::parser_t
{
public:
virtual bool test(std::istream& in) const;
diff --git a/report.cc b/report.cc
index e2d80d94..ff8000ee 100644
--- a/report.cc
+++ b/report.cc
@@ -278,7 +278,7 @@ void report_t::entry_report(const entry_t& entry, const string& format)
{
}
-value_t report_t::abbrev(expr::call_scope_t& args)
+value_t report_t::abbrev(call_scope_t& args)
{
if (args.size() < 2)
throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
@@ -304,7 +304,7 @@ value_t report_t::abbrev(expr::call_scope_t& args)
#endif
}
-value_t report_t::ftime(expr::call_scope_t& args)
+value_t report_t::ftime(call_scope_t& args)
{
if (args.size() < 1)
throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
@@ -327,7 +327,7 @@ value_t report_t::ftime(expr::call_scope_t& args)
#if 0
optional<value_t>
-report_t::resolve(const string& name, expr::call_scope_t& args)
+report_t::resolve(const string& name, call_scope_t& args)
{
const char * p = name.c_str();
switch (*p) {
@@ -343,11 +343,11 @@ report_t::resolve(const string& name, expr::call_scope_t& args)
}
break;
}
- return expr::scope_t::resolve(name, args);
+ return scope_t::resolve(name, args);
}
#endif
-expr::ptr_op_t report_t::lookup(const string& name)
+expr_t::ptr_op_t report_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
@@ -453,7 +453,7 @@ expr::ptr_op_t report_t::lookup(const string& name)
break;
}
- return expr::symbol_scope_t::lookup(name);
+ return symbol_scope_t::lookup(name);
}
} // namespace ledger
diff --git a/report.h b/report.h
index cbe53e3e..0a1366e1 100644
--- a/report.h
+++ b/report.h
@@ -81,7 +81,7 @@ namespace ledger {
// says that the formatter should be "flushed" after the entities are
// iterated. This does not happen for the commodities iteration, however.
-class report_t : public expr::symbol_scope_t
+class report_t : public symbol_scope_t
{
report_t();
@@ -134,7 +134,7 @@ public:
session_t& session;
explicit report_t(session_t& _session)
- : expr::symbol_scope_t(downcast<expr::scope_t>(_session)),
+ : symbol_scope_t(downcast<scope_t>(_session)),
head_entries(0),
tail_entries(0),
@@ -166,10 +166,10 @@ public:
#if 0
eval("t=total,TOT=0,T()=(TOT=TOT+t,TOT)");
-#endif
- value_expr::amount_expr.reset(new value_expr("a"));
- value_expr::total_expr.reset(new value_expr("O"));
+ value_expr_t::amount_expr.reset(new value_expr("a"));
+ value_expr_t::total_expr.reset(new value_expr("O"));
+#endif
}
virtual ~report_t() {
@@ -201,8 +201,8 @@ public:
// Utility functions for value expressions
//
- value_t ftime(expr::call_scope_t& args);
- value_t abbrev(expr::call_scope_t& args);
+ value_t ftime(call_scope_t& args);
+ value_t abbrev(call_scope_t& args);
//
// Config options
@@ -213,35 +213,35 @@ public:
expr(expr).compile((xml::document_t *)NULL, this);
#endif
}
- value_t option_eval(expr::call_scope_t& args) {
+ value_t option_eval(call_scope_t& args) {
eval(args[0].as_string());
return NULL_VALUE;
}
- value_t option_amount(expr::call_scope_t& args) {
+ value_t option_amount(call_scope_t& args) {
eval(string("t=") + args[0].as_string());
return NULL_VALUE;
}
- value_t option_total(expr::call_scope_t& args) {
+ value_t option_total(call_scope_t& args) {
eval(string("T()=") + args[0].as_string());
return NULL_VALUE;
}
- value_t option_format(expr::call_scope_t& args) {
+ value_t option_format(call_scope_t& args) {
format_string = args[0].as_string();
return NULL_VALUE;
}
- value_t option_raw(expr::call_scope_t& args) {
+ value_t option_raw(call_scope_t& args) {
raw_mode = true;
return NULL_VALUE;
}
- value_t option_foo(expr::call_scope_t& args) {
+ value_t option_foo(call_scope_t& args) {
std::cout << "This is foo" << std::endl;
return NULL_VALUE;
}
- value_t option_bar(expr::call_scope_t& args) {
+ value_t option_bar(call_scope_t& args) {
std::cout << "This is bar: " << args[0] << std::endl;
return NULL_VALUE;
}
@@ -251,44 +251,44 @@ public:
//
#if 0
- value_t option_select(expr::call_scope_t& args) {
+ value_t option_select(call_scope_t& args) {
transforms.push_back(new select_transform(args[0].as_string()));
return NULL_VALUE;
}
- value_t option_limit(expr::call_scope_t& args) {
+ value_t option_limit(call_scope_t& args) {
string expr = (string("//xact[") +
args[0].as_string() + "]");
transforms.push_back(new select_transform(expr));
return NULL_VALUE;
}
- value_t option_remove(expr::call_scope_t& args) {
+ value_t option_remove(call_scope_t& args) {
transforms.push_back(new remove_transform(args[0].as_string()));
return NULL_VALUE;
}
- value_t option_accounts(expr::call_scope_t& args) {
+ value_t option_accounts(call_scope_t& args) {
transforms.push_back(new accounts_transform);
return NULL_VALUE;
}
- value_t option_compact(expr::call_scope_t& args) {
+ value_t option_compact(call_scope_t& args) {
transforms.push_back(new compact_transform);
return NULL_VALUE;
}
- value_t option_clean(expr::call_scope_t& args) {
+ value_t option_clean(call_scope_t& args) {
transforms.push_back(new clean_transform);
return NULL_VALUE;
}
- value_t option_entries(expr::call_scope_t& args) {
+ value_t option_entries(call_scope_t& args) {
transforms.push_back(new entries_transform);
return NULL_VALUE;
}
- value_t option_split(expr::call_scope_t& args) {
+ value_t option_split(call_scope_t& args) {
transforms.push_back(new split_transform);
return NULL_VALUE;
}
- value_t option_merge(expr::call_scope_t& args) {
+ value_t option_merge(call_scope_t& args) {
transforms.push_back(new merge_transform);
return NULL_VALUE;
}
@@ -298,7 +298,7 @@ public:
// Scope members
//
- virtual expr::ptr_op_t lookup(const string& name);
+ virtual expr_t::ptr_op_t lookup(const string& name);
};
string abbrev(const string& str, unsigned int width,
diff --git a/scope.cc b/scope.cc
new file mode 100644
index 00000000..872c5454
--- /dev/null
+++ b/scope.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#include "scope.h"
+
+namespace ledger {
+
+void symbol_scope_t::define(const string& name, expr_t::ptr_op_t def)
+{
+ DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def);
+
+ std::pair<symbol_map::iterator, bool> result
+ = symbols.insert(symbol_map::value_type(name, def));
+ if (! result.second) {
+ symbol_map::iterator i = symbols.find(name);
+ assert(i != symbols.end());
+ symbols.erase(i);
+
+ std::pair<symbol_map::iterator, bool> result2
+ = symbols.insert(symbol_map::value_type(name, def));
+ if (! result2.second)
+ throw_(compile_error,
+ "Redefinition of '" << name << "' in same scope");
+ }
+}
+
+value_t get_amount(scope_t& scope)
+{
+ assert("I can't get the amount!");
+ return NULL_VALUE;
+}
+
+expr_t::ptr_op_t symbol_scope_t::lookup(const string& name)
+{
+ switch (name[0]) {
+ case 'a':
+ if (name[1] == '\0' || name == "amount")
+ return WRAP_FUNCTOR(bind(get_amount, _1));
+ break;
+ }
+
+ symbol_map::const_iterator i = symbols.find(name);
+ if (i != symbols.end())
+ return (*i).second;
+
+ return child_scope_t::lookup(name);
+}
+
+#if 0
+namespace {
+ int count_leaves(expr_t::ptr_op_t expr)
+ {
+ int count = 0;
+ if (expr->kind != expr_t::op_t::O_COMMA) {
+ count = 1;
+ } else {
+ count += count_leaves(expr->left());
+ count += count_leaves(expr->right());
+ }
+ return count;
+ }
+
+ expr_t::ptr_op_t reduce_leaves(expr_t::ptr_op_t expr,
+ expr_t::ptr_op_t context)
+ {
+ if (! expr)
+ return NULL;
+
+ expr_t::ptr_op_t temp;
+
+ if (expr->kind != expr_t::op_t::O_COMMA) {
+ if (expr->kind < expr_t::op_t::TERMINALS) {
+ temp.reset(expr);
+ } else {
+ temp.reset(new op_t(expr_t::op_t::VALUE));
+ temp->set_value(NULL_VALUE);
+ expr->compute(temp->as_value_lval(), context);
+ }
+ } else {
+ temp.reset(new op_t(expr_t::op_t::O_COMMA));
+ temp->set_left(reduce_leaves(expr->left(), context));
+ temp->set_right(reduce_leaves(expr->right(), context));
+ }
+ return temp.release();
+ }
+
+ expr_t::ptr_op_t find_leaf(expr_t::ptr_op_t context, int goal, long& found)
+ {
+ if (! context)
+ return NULL;
+
+ if (context->kind != expr_t::op_t::O_COMMA) {
+ if (goal == found++)
+ return context;
+ } else {
+ expr_t::ptr_op_t expr = find_leaf(context->left(), goal, found);
+ if (expr)
+ return expr;
+ expr = find_leaf(context->right(), goal, found);
+ if (expr)
+ return expr;
+ }
+ return NULL;
+ }
+}
+#endif
+
+} // namespace ledger
diff --git a/scope.h b/scope.h
new file mode 100644
index 00000000..4a031104
--- /dev/null
+++ b/scope.h
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _SCOPE_H
+#define _SCOPE_H
+
+#include "expr.h"
+#include "op.h"
+
+namespace ledger {
+
+class scope_t : public noncopyable
+{
+ scope_t();
+
+protected:
+ enum type_t {
+ CHILD_SCOPE,
+ SYMBOL_SCOPE,
+ CALL_SCOPE,
+ CONTEXT_SCOPE
+ } type_;
+
+public:
+ explicit scope_t(type_t _type) : type_(_type) {
+ TRACE_CTOR(scope_t, "type_t");
+ }
+ virtual ~scope_t() {
+ TRACE_DTOR(scope_t);
+ }
+
+ const type_t type() const {
+ return type_;
+ }
+
+ void define(const string& name, const value_t& val) {
+ define(name, expr_t::op_t::wrap_value(val));
+ }
+ void define(const string& name, const function_t& func) {
+ define(name, expr_t::op_t::wrap_functor(func));
+ }
+
+ value_t resolve(const string& name) {
+ expr_t::ptr_op_t definition = lookup(name);
+ if (definition)
+ return definition->calc(*this);
+ else
+ return NULL_VALUE;
+ }
+
+ virtual void define(const string& name, expr_t::ptr_op_t def) = 0;
+ virtual expr_t::ptr_op_t lookup(const string& name) = 0;
+
+protected:
+ virtual optional<scope_t&> find_scope(const type_t _type,
+ bool skip_this = false) = 0;
+ virtual optional<scope_t&> find_first_scope(const type_t _type1,
+ const type_t _type2,
+ bool skip_this = false) = 0;
+ template <typename T>
+ T& find_scope(bool skip_this = false) {
+ assert(false);
+ }
+ template <typename T>
+ optional<T&> maybe_find_scope(bool skip_this = false) {
+ assert(false);
+ }
+
+ friend class child_scope_t;
+ friend class expr_t::op_t;
+};
+
+class child_scope_t : public scope_t
+{
+ scope_t * parent;
+
+ child_scope_t();
+
+public:
+ explicit child_scope_t(type_t _type = CHILD_SCOPE)
+ : scope_t(_type), parent(NULL) {
+ TRACE_CTOR(child_scope_t, "type_t");
+ }
+ explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
+ : scope_t(_type), parent(&_parent) {
+ TRACE_CTOR(child_scope_t, "scope_t&, type_t");
+ }
+ virtual ~child_scope_t() {
+ TRACE_DTOR(child_scope_t);
+ }
+
+ virtual void define(const string& name, expr_t::ptr_op_t def) {
+ if (parent)
+ parent->define(name, def);
+ }
+ virtual expr_t::ptr_op_t lookup(const string& name) {
+ if (parent)
+ return parent->lookup(name);
+ return expr_t::ptr_op_t();
+ }
+
+protected:
+ virtual optional<scope_t&> find_scope(type_t _type,
+ bool skip_this = false) {
+ for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
+ if (ptr->type() == _type)
+ return *ptr;
+
+ ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
+ }
+ return none;
+ }
+
+ virtual optional<scope_t&> find_first_scope(const type_t _type1,
+ const type_t _type2,
+ bool skip_this = false) {
+ for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
+ if (ptr->type() == _type1 || ptr->type() == _type2)
+ return *ptr;
+
+ ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
+ }
+ return none;
+ }
+};
+
+class symbol_scope_t : public child_scope_t
+{
+ typedef std::map<const string, expr_t::ptr_op_t> symbol_map;
+
+ symbol_map symbols;
+
+public:
+ explicit symbol_scope_t()
+ : child_scope_t(SYMBOL_SCOPE) {
+ TRACE_CTOR(symbol_scope_t, "");
+ }
+ explicit symbol_scope_t(scope_t& _parent)
+ : child_scope_t(_parent, SYMBOL_SCOPE) {
+ TRACE_CTOR(symbol_scope_t, "scope_t&");
+ }
+ virtual ~symbol_scope_t() {
+ TRACE_DTOR(symbol_scope_t);
+ }
+
+ void define(const string& name, const value_t& val) {
+ scope_t::define(name, val);
+ }
+ void define(const string& name, const function_t& func) {
+ scope_t::define(name, func);
+ }
+
+ virtual void define(const string& name, expr_t::ptr_op_t def);
+ virtual expr_t::ptr_op_t lookup(const string& name);
+};
+
+class call_scope_t : public child_scope_t
+{
+ value_t args;
+
+ call_scope_t();
+
+public:
+ explicit call_scope_t(scope_t& _parent)
+ : child_scope_t(_parent, CALL_SCOPE) {
+ TRACE_CTOR(call_scope_t, "scope_t&");
+ }
+ virtual ~call_scope_t() {
+ TRACE_DTOR(call_scope_t);
+ }
+
+ void set_args(const value_t& _args) {
+ args = _args;
+ }
+ value_t& value() {
+ return args;
+ }
+
+ value_t& operator[](const unsigned int index) {
+ // jww (2008-07-21): exception here if it's out of bounds
+ return args[index];
+ }
+ const value_t& operator[](const unsigned int index) const {
+ // jww (2008-07-21): exception here if it's out of bounds
+ return args[index];
+ }
+
+ void push_back(const value_t& val) {
+ args.push_back(val);
+ }
+ void pop_back() {
+ args.pop_back();
+ }
+
+ const std::size_t size() const {
+ return args.size();
+ }
+};
+
+template <typename T>
+class var_t : public noncopyable
+{
+ T * value;
+
+ var_t();
+
+public:
+ // jww (2008-07-21): Give a good exception here if we can't find "name"
+ var_t(scope_t& scope, const string& name)
+ : value(scope.resolve(name).template as_pointer<T>()) {
+ TRACE_CTOR(var_t, "scope_t&, const string&");
+ }
+ var_t(call_scope_t& scope, const unsigned int idx)
+ : value(scope[idx].template as_pointer<T>()) {
+ TRACE_CTOR(var_t, "call_scope_t&, const unsigned int");
+ }
+ ~var_t() throw() {
+ TRACE_DTOR(var_t);
+ }
+
+ T& operator *() { return *value; }
+ T * operator->() { return value; }
+};
+
+#if 0
+class context_scope_t : public child_scope_t
+{
+public:
+ value_t current_element;
+ std::size_t element_index;
+ std::size_t sequence_size;
+
+ explicit context_scope_t(scope_t& _parent,
+ const value_t& _element = NULL_VALUE,
+ const std::size_t _element_index = 0,
+ const std::size_t _sequence_size = 0)
+ : child_scope_t(_parent, CONTEXT_SCOPE), current_element(_element),
+ element_index(_element_index), sequence_size(_sequence_size)
+ {
+ TRACE_CTOR(expr::context_scope_t, "scope_t&, const value_t&, ...");
+ }
+ virtual ~context_scope_t() {
+ TRACE_DTOR(expr::context_scope_t);
+ }
+
+ const std::size_t index() const {
+ return element_index;
+ }
+ const std::size_t size() const {
+ return sequence_size;
+ }
+
+ value_t& value() {
+ return current_element;
+ }
+};
+
+struct context_t
+{
+ const entry_t * entry() {
+ return NULL;
+ }
+ const transaction_t * xact() {
+ return NULL;
+ }
+ const account_t * account() {
+ return NULL;
+ }
+};
+
+struct entry_context_t : public context_t
+{
+ const entry_t * entry_;
+
+ const entry_t * entry() {
+ return entry_;
+ }
+};
+
+struct xact_context_t : public context_t
+{
+ const transaction_t * xact_;
+
+ const entry_t * entry() {
+ return xact_->entry;
+ }
+ const transaction_t * xact() {
+ return xact_;
+ }
+ const account_t * account() {
+ return xact_->account;
+ }
+};
+
+struct account_context_t : public context_t
+{
+ const account_t * account_;
+
+ const account_t * account() {
+ return account_;
+ }
+};
+#endif
+
+template<>
+inline symbol_scope_t&
+scope_t::find_scope<symbol_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(SYMBOL_SCOPE, skip_this);
+ assert(scope);
+ return downcast<symbol_scope_t>(*scope);
+}
+
+template<>
+inline call_scope_t&
+scope_t::find_scope<call_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(CALL_SCOPE, skip_this);
+ assert(scope);
+ return downcast<call_scope_t>(*scope);
+}
+
+#if 0
+template<>
+inline context_scope_t&
+scope_t::find_scope<context_scope_t>(bool skip_this) {
+ optional<scope_t&> scope = find_scope(CONTEXT_SCOPE, skip_this);
+ assert(scope);
+ return downcast<context_scope_t>(*scope);
+}
+#endif
+
+#define FIND_SCOPE(scope_type, scope_ref) \
+ downcast<scope_t>(scope_ref).find_scope<scope_type>()
+
+#define CALL_SCOPE(scope_ref) \
+ FIND_SCOPE(call_scope_t, scope_ref)
+#define SYMBOL_SCOPE(scope_ref) \
+ FIND_SCOPE(symbol_scope_t, scope_ref)
+#if 0
+#define CONTEXT_SCOPE(scope_ref) \
+ FIND_SCOPE(context_scope_t, scope_ref)
+#endif
+
+} // namespace ledger
+
+#endif // _SCOPE_H
+
diff --git a/session.cc b/session.cc
index 57e965e9..4a565a9e 100644
--- a/session.cc
+++ b/session.cc
@@ -30,7 +30,6 @@
*/
#include "session.h"
-#include "parsexp.h"
#include "walk.h"
namespace ledger {
@@ -41,9 +40,6 @@ session_t * session_t::current = NULL;
boost::mutex session_t::session_mutex;
#endif
-static void initialize();
-static void shutdown();
-
void set_session_context(session_t * session)
{
#if 0
@@ -51,10 +47,10 @@ void set_session_context(session_t * session)
#endif
if (session && ! session_t::current) {
- initialize();
+ session_t::initialize();
}
else if (! session && session_t::current) {
- shutdown();
+ session_t::shutdown();
#if 0
session_t::session_mutex.unlock();
#endif
@@ -127,7 +123,7 @@ std::size_t session_t::read_journal(journal_t& journal,
if (! master)
master = journal.master;
- foreach (parser_t& parser, parsers)
+ foreach (journal_t::parser_t& parser, parsers)
if (parser.test(in))
return parser.parse(in, *this, journal, master, &pathname);
@@ -264,7 +260,7 @@ void session_t::clean_accounts()
}
#if 0
-value_t session_t::resolve(const string& name, expr::scope_t& locals)
+value_t session_t::resolve(const string& name, expr_t::scope_t& locals)
{
const char * p = name.c_str();
switch (*p) {
@@ -291,11 +287,11 @@ value_t session_t::resolve(const string& name, expr::scope_t& locals)
return value_t(register_format, true);
break;
}
- return expr::scope_t::resolve(name, locals);
+ return expr_t::scope_t::resolve(name, locals);
}
#endif
-expr::ptr_op_t session_t::lookup(const string& name)
+expr_t::ptr_op_t session_t::lookup(const string& name)
{
const char * p = name.c_str();
switch (*p) {
@@ -332,21 +328,21 @@ expr::ptr_op_t session_t::lookup(const string& name)
break;
}
- return expr::symbol_scope_t::lookup(name);
+ return symbol_scope_t::lookup(name);
}
// jww (2007-04-26): All of Ledger should be accessed through a
// session_t object
-static void initialize()
+void session_t::initialize()
{
amount_t::initialize();
value_t::initialize();
- value_expr::initialize();
+ expr_t::initialize();
}
-static void shutdown()
+void session_t::shutdown()
{
- value_expr::shutdown();
+ expr_t::shutdown();
value_t::shutdown();
amount_t::shutdown();
}
diff --git a/session.h b/session.h
index 1534cd0b..3c1bc89a 100644
--- a/session.h
+++ b/session.h
@@ -32,15 +32,20 @@
#ifndef _SESSION_H
#define _SESSION_H
-#include "valexpr.h"
+#include "scope.h"
#include "journal.h"
-#include "parser.h"
namespace ledger {
-class session_t : public expr::symbol_scope_t
+class session_t : public symbol_scope_t
{
- public:
+ static void initialize();
+ static void shutdown();
+
+ friend void set_session_context(session_t * session);
+ friend void release_session_context();
+
+public:
static session_t * current;
path data_file;
@@ -76,8 +81,8 @@ class session_t : public expr::symbol_scope_t
bool ansi_codes;
bool ansi_invert;
- ptr_list<journal_t> journals;
- ptr_list<parser_t> parsers;
+ ptr_list<journal_t> journals;
+ ptr_list<journal_t::parser_t> parsers;
account_t * master;
mutable accounts_map accounts_cache;
@@ -121,11 +126,11 @@ class session_t : public expr::symbol_scope_t
std::size_t read_data(journal_t& journal,
const string& master_account = "");
- void register_parser(parser_t * parser) {
+ void register_parser(journal_t::parser_t * parser) {
parsers.push_back(parser);
}
- void unregister_parser(parser_t * parser) {
- for (ptr_list<parser_t>::iterator i = parsers.begin();
+ void unregister_parser(journal_t::parser_t * parser) {
+ for (ptr_list<journal_t::parser_t>::iterator i = parsers.begin();
i != parsers.end();
i++)
if (&*i == parser) {
@@ -167,13 +172,13 @@ class session_t : public expr::symbol_scope_t
// Scope members
//
- virtual expr::ptr_op_t lookup(const string& name);
+ virtual expr_t::ptr_op_t lookup(const string& name);
//
// Help options
//
- value_t option_version(expr::scope_t&) {
+ value_t option_version(scope_t&) {
std::cout << "Ledger " << ledger::version << ", the command-line accounting tool";
std::cout << "\n\nCopyright (c) 2003-2008, John Wiegley. All rights reserved.\n\n\
This program is made available under the terms of the BSD Public License.\n\
@@ -193,17 +198,17 @@ See LICENSE file included with the distribution for details and disclaimer.\n";
// Debug options
//
- value_t option_trace_(expr::scope_t& locals) {
+ value_t option_trace_(scope_t& locals) {
return NULL_VALUE;
}
- value_t option_debug_(expr::scope_t& locals) {
+ value_t option_debug_(scope_t& locals) {
return NULL_VALUE;
}
- value_t option_verify(expr::scope_t&) {
+ value_t option_verify(scope_t&) {
return NULL_VALUE;
}
- value_t option_verbose(expr::scope_t&) {
+ value_t option_verbose(scope_t&) {
#if defined(LOGGING_ON)
if (_log_level < LOG_INFO)
_log_level = LOG_INFO;
@@ -215,7 +220,7 @@ See LICENSE file included with the distribution for details and disclaimer.\n";
// Option handlers
//
- value_t option_file_(expr::call_scope_t& args) {
+ value_t option_file_(call_scope_t& args) {
assert(args.size() == 1);
data_file = args[0].as_string();
return NULL_VALUE;
@@ -223,11 +228,11 @@ See LICENSE file included with the distribution for details and disclaimer.\n";
#if 0
#if defined(USE_BOOST_PYTHON)
- value_t option_import_(expr::call_scope_t& args) {
+ value_t option_import_(call_scope_t& args) {
python_import(optarg);
return NULL_VALUE;
}
- value_t option_import_stdin(expr::call_scope_t& args) {
+ value_t option_import_stdin(call_scope_t& args) {
python_eval(std::cin, PY_EVAL_MULTI);
return NULL_VALUE;
}
diff --git a/test/numerics/t_expr.cc b/test/numerics/t_expr.cc
new file mode 100644
index 00000000..026b4eec
--- /dev/null
+++ b/test/numerics/t_expr.cc
@@ -0,0 +1,25 @@
+#include "t_valexpr.h"
+
+CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(ValueExprTestCase, "numerics");
+
+void ValueExprTestCase::setUp()
+{
+ ledger::set_session_context(&session);
+
+ // Cause the display precision for dollars to be initialized to 2.
+ amount_t x1("$1.00");
+ assertTrue(x1);
+
+ amount_t::stream_fullstrings = true; // make reports from UnitTests accurate
+}
+
+void ValueExprTestCase::tearDown()
+{
+ amount_t::stream_fullstrings = false;
+
+ ledger::set_session_context();
+}
+
+void ValueExprTestCase::testConstructors()
+{
+}
diff --git a/test/numerics/t_expr.h b/test/numerics/t_expr.h
new file mode 100644
index 00000000..3cac4ed6
--- /dev/null
+++ b/test/numerics/t_expr.h
@@ -0,0 +1,30 @@
+#ifndef _T_VALEXPR_H
+#define _T_VALEXPR_H
+
+#include "UnitTests.h"
+
+class ValueExprTestCase : public CPPUNIT_NS::TestCase
+{
+ CPPUNIT_TEST_SUITE(ValueExprTestCase);
+
+ CPPUNIT_TEST(testConstructors);
+
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+ ledger::session_t session;
+
+ ValueExprTestCase() {}
+ virtual ~ValueExprTestCase() {}
+
+ virtual void setUp();
+ virtual void tearDown();
+
+ void testConstructors();
+
+private:
+ ValueExprTestCase(const ValueExprTestCase &copy);
+ void operator=(const ValueExprTestCase &copy);
+};
+
+#endif // _T_VALEXPR_H
diff --git a/textual.cc b/textual.cc
index 1027814b..c309801e 100644
--- a/textual.cc
+++ b/textual.cc
@@ -2,11 +2,9 @@
#define _XOPEN_SOURCE
#endif
-#include "journal.h"
#include "textual.h"
-#include "valexpr.h"
-#include "parsexp.h"
-#include "utils.h"
+#include "expr.h"
+#include "parser.h"
#include "acconf.h"
#define TIMELOG_SUPPORT 1
@@ -50,14 +48,12 @@ struct time_entry_t
#endif
namespace {
- value_expr parse_amount_expr(std::istream& in,
- amount_t& amount,
- transaction_t * xact,
- unsigned short flags = 0)
+ expr_t parse_amount_expr(std::istream& in,
+ amount_t& amount,
+ transaction_t * xact,
+ unsigned short flags = 0)
{
- value_expr expr =
- value_expr::parser->parse(in, flags |
- EXPR_PARSE_RELAXED | EXPR_PARSE_PARTIAL);
+ expr_t expr(in, flags | EXPR_PARSE_PARTIAL);
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
"Parsed an amount expression");
@@ -71,12 +67,13 @@ namespace {
}
#endif
+#if 0
if (expr) {
- if (! expr::compute_amount(expr, amount, xact))
+ if (! expr_t::compute_amount(expr, amount, xact))
throw new parse_error("Amount expression failed to compute");
#if 0
- if (expr->kind == expr::node_t::VALUE) {
+ if (expr->kind == expr_t::node_t::VALUE) {
expr = NULL;
} else {
DEBUG_IF("ledger.textual.parse") {
@@ -88,6 +85,7 @@ namespace {
expr = value_expr();
#endif
}
+#endif
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
"The transaction amount is " << xact->amount);
@@ -201,7 +199,7 @@ transaction_t * parse_transaction(char * line, account_t * account,
// always NULL right now
if (xact->amount_expr) {
unsigned long end = (long)in.tellg();
- xact->amount_expr.expr_str = string(line, beg, end - beg);
+ xact->amount_expr.set_text(string(line, beg, end - beg));
}
}
catch (error * err) {
@@ -247,11 +245,11 @@ transaction_t * parse_transaction(char * line, account_t * account,
if (xact->cost_expr) {
unsigned long end = (long)in.tellg();
if (per_unit)
- xact->cost_expr->expr_str = (string("@") +
- string(line, beg, end - beg));
+ xact->cost_expr->set_text(string("@") +
+ string(line, beg, end - beg));
else
- xact->cost_expr->expr_str = (string("@@") +
- string(line, beg, end - beg));
+ xact->cost_expr->set_text(string("@@") +
+ string(line, beg, end - beg));
}
}
catch (error * err) {
@@ -951,7 +949,7 @@ unsigned int textual_parser_t::parse(std::istream& in,
}
else if (word == "def") {
#if 0
- if (! expr::global_scope.get())
+ if (! expr_t::global_scope.get())
init_value_expr();
parse_value_definition(p);
#endif
@@ -1088,11 +1086,13 @@ void write_textual_journal(journal_t& journal, path pathname,
while (! in.eof()) {
entry_base_t * base = NULL;
if (el != journal.entries.end() && pos == (*el)->beg_pos) {
+#if 0
hdr_fmt.format(out, details_t(**el));
+#endif
base = *el++;
}
else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) {
- out << "= " << (*al)->predicate.predicate.expr_str << '\n';
+ out << "= " << (*al)->predicate.predicate.text() << '\n';
base = *al++;
}
else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) {
diff --git a/textual.h b/textual.h
index 1c5e3aa2..71e44a94 100644
--- a/textual.h
+++ b/textual.h
@@ -1,13 +1,13 @@
#ifndef _TEXTUAL_H
#define _TEXTUAL_H
-#include "parser.h"
+#include "journal.h"
#include "format.h"
#include "walk.h"
namespace ledger {
-class textual_parser_t : public parser_t
+class textual_parser_t : public journal_t::parser_t
{
public:
virtual bool test(std::istream& in) const;
diff --git a/token.cc b/token.cc
new file mode 100644
index 00000000..3d5eeb21
--- /dev/null
+++ b/token.cc
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2003-2008, 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.
+ */
+
+#include "token.h"
+#include "parser.h"
+
+namespace ledger {
+
+void expr_t::token_t::parse_ident(std::istream& in)
+{
+ if (in.eof()) {
+ kind = TOK_EOF;
+ return;
+ }
+ assert(in.good());
+
+ char c = peek_next_nonws(in);
+
+ if (in.eof()) {
+ kind = TOK_EOF;
+ return;
+ }
+ assert(in.good());
+
+ kind = IDENT;
+ length = 0;
+
+ clear_flags();
+
+ char buf[256];
+ READ_INTO_(in, buf, 255, c, length,
+ std::isalnum(c) || c == '_' || c == '.' || c == '-');
+
+ switch (buf[0]) {
+#if 0
+ case 'a':
+ if (std::strcmp(buf, "and") == 0)
+ kind = KW_AND;
+ break;
+ case 'd':
+ if (std::strcmp(buf, "div") == 0)
+ kind = KW_DIV;
+ break;
+ case 'e':
+ if (std::strcmp(buf, "eq") == 0)
+ kind = EQUAL;
+ break;
+#endif
+ case 'f':
+ if (std::strcmp(buf, "false") == 0) {
+ kind = VALUE;
+ value = false;
+ }
+ break;
+#if 0
+ case 'g':
+ if (std::strcmp(buf, "gt") == 0)
+ kind = GREATER;
+ else if (std::strcmp(buf, "ge") == 0)
+ kind = GREATEREQ;
+ break;
+ case 'i':
+ if (std::strcmp(buf, "is") == 0)
+ kind = EQUAL;
+ break;
+ case 'l':
+ if (std::strcmp(buf, "lt") == 0)
+ kind = LESS;
+ else if (std::strcmp(buf, "le") == 0)
+ kind = LESSEQ;
+ break;
+ case 'm':
+ if (std::strcmp(buf, "mod") == 0)
+ kind = KW_MOD;
+ break;
+ case 'n':
+ if (std::strcmp(buf, "ne") == 0)
+ kind = NEQUAL;
+ break;
+ case 'o':
+ if (std::strcmp(buf, "or") == 0)
+ kind = KW_OR;
+ break;
+#endif
+ case 't':
+ if (std::strcmp(buf, "true") == 0) {
+ kind = VALUE;
+ value = true;
+ }
+ break;
+ }
+
+ if (kind == IDENT)
+ value.set_string(buf);
+}
+
+void expr_t::token_t::next(std::istream& in, const unsigned int pflags)
+{
+ if (in.eof()) {
+ kind = TOK_EOF;
+ return;
+ }
+ assert(in.good());
+
+ char c = peek_next_nonws(in);
+
+ if (in.eof()) {
+ kind = TOK_EOF;
+ return;
+ }
+ assert(in.good());
+
+ symbol[0] = c;
+ symbol[1] = '\0';
+
+ length = 1;
+
+ switch (c) {
+ case '&':
+ in.get(c);
+ kind = KW_AND;
+ break;
+
+ case '(':
+ in.get(c);
+ kind = LPAREN;
+ break;
+ case ')':
+ in.get(c);
+ kind = RPAREN;
+ break;
+
+ case '[': {
+ in.get(c);
+
+ char buf[256];
+ READ_INTO_(in, buf, 255, c, length, c != ']');
+ if (c != ']')
+ unexpected(c, ']');
+
+ in.get(c);
+ length++;
+
+ interval_t timespan(buf);
+ kind = VALUE;
+ value = timespan.first();
+ break;
+ }
+
+ case '\'':
+ case '"': {
+ char delim;
+ in.get(delim);
+ char buf[4096];
+ READ_INTO_(in, buf, 4095, c, length, c != delim);
+ if (c != delim)
+ unexpected(c, delim);
+ in.get(c);
+ length++;
+ kind = VALUE;
+ value.set_string(buf);
+ break;
+ }
+
+ case '{': {
+ in.get(c);
+ amount_t temp;
+ temp.parse(in, AMOUNT_PARSE_NO_MIGRATE);
+ in.get(c);
+ if (c != '}')
+ unexpected(c, '}');
+ length++;
+ kind = VALUE;
+ value = temp;
+ break;
+ }
+
+ case '!':
+ in.get(c);
+ c = in.peek();
+ if (c == '=') {
+ in.get(c);
+ symbol[1] = c;
+ symbol[2] = '\0';
+ kind = NEQUAL;
+ length = 2;
+ break;
+ }
+ kind = EXCLAM;
+ break;
+
+ case '-':
+ in.get(c);
+ kind = MINUS;
+ break;
+ case '+':
+ in.get(c);
+ kind = PLUS;
+ break;
+
+ case '*':
+ in.get(c);
+ kind = STAR;
+ break;
+
+ case 'c':
+ case 'C':
+ case 'p':
+ case 'w':
+ case 'W':
+ case 'e':
+ case '/': {
+ bool code_mask = c == 'c';
+ bool commodity_mask = c == 'C';
+ bool payee_mask = c == 'p';
+ bool note_mask = c == 'e';
+ bool short_account_mask = c == 'w';
+
+ in.get(c);
+ if (c == '/') {
+ c = peek_next_nonws(in);
+ if (c == '/') {
+ in.get(c);
+ c = in.peek();
+ if (c == '/') {
+ in.get(c);
+ c = in.peek();
+ short_account_mask = true;
+ } else {
+ payee_mask = true;
+ }
+ }
+ } else {
+ in.get(c);
+ }
+
+ // Read in the regexp
+ char buf[256];
+ READ_INTO_(in, buf, 255, c, length, c != '/');
+ if (c != '/')
+ unexpected(c, '/');
+ in.get(c);
+ length++;
+
+ if (short_account_mask)
+ set_flags(TOKEN_SHORT_ACCOUNT_MASK);
+ else if (code_mask)
+ set_flags(TOKEN_CODE_MASK);
+ else if (commodity_mask)
+ set_flags(TOKEN_COMMODITY_MASK);
+ else if (payee_mask)
+ set_flags(TOKEN_PAYEE_MASK);
+ else if (note_mask)
+ set_flags(TOKEN_NOTE_MASK);
+ else
+ set_flags(TOKEN_ACCOUNT_MASK);
+
+ kind = MASK;
+ value.set_string(buf);
+ break;
+ }
+
+ case '=':
+ in.get(c);
+ kind = EQUAL;
+ break;
+
+ case '<':
+ in.get(c);
+ if (in.peek() == '=') {
+ in.get(c);
+ symbol[1] = c;
+ symbol[2] = '\0';
+ kind = LESSEQ;
+ length = 2;
+ break;
+ }
+ kind = LESS;
+ break;
+
+ case '>':
+ in.get(c);
+ if (in.peek() == '=') {
+ in.get(c);
+ symbol[1] = c;
+ symbol[2] = '\0';
+ kind = GREATEREQ;
+ length = 2;
+ break;
+ }
+ kind = GREATER;
+ break;
+
+ case ',':
+ in.get(c);
+ kind = COMMA;
+ break;
+
+ default: {
+ amount_t temp;
+ unsigned long pos = 0;
+
+ // When in relaxed parsing mode, we want to migrate commodity
+ // flags so that any precision specified by the user updates the
+ // current maximum displayed precision.
+ pos = (long)in.tellg();
+
+ amount_t::flags_t parse_flags = 0;
+ if (pflags & EXPR_PARSE_NO_MIGRATE)
+ parse_flags |= AMOUNT_PARSE_NO_MIGRATE;
+ if (pflags & EXPR_PARSE_NO_REDUCE)
+ parse_flags |= AMOUNT_PARSE_NO_REDUCE;
+
+ if (! temp.parse(in, parse_flags | AMOUNT_PARSE_SOFT_FAIL)) {
+ // If the amount had no commodity, it must be an unambiguous
+ // variable reference
+
+ in.clear();
+ in.seekg(pos, std::ios::beg);
+
+ c = in.peek();
+ assert(! (std::isdigit(c) || c == '.'));
+ parse_ident(in);
+ } else {
+ kind = VALUE;
+ value = temp;
+ }
+ break;
+ }
+ }
+}
+
+void expr_t::token_t::rewind(std::istream& in)
+{
+ for (unsigned int i = 0; i < length; i++)
+ in.unget();
+}
+
+
+void expr_t::token_t::unexpected()
+{
+ switch (kind) {
+ case TOK_EOF:
+ throw_(parse_error, "Unexpected end of expression");
+ case IDENT:
+ throw_(parse_error, "Unexpected symbol '" << value << "'");
+ case VALUE:
+ throw_(parse_error, "Unexpected value '" << value << "'");
+ default:
+ throw_(parse_error, "Unexpected operator '" << symbol << "'");
+ }
+}
+
+void expr_t::token_t::unexpected(char c, char wanted)
+{
+ if ((unsigned char) c == 0xff) {
+ if (wanted)
+ throw_(parse_error, "Missing '" << wanted << "'");
+ else
+ throw_(parse_error, "Unexpected end");
+ } else {
+ if (wanted)
+ throw_(parse_error, "Invalid char '" << c
+ << "' (wanted '" << wanted << "')");
+ else
+ throw_(parse_error, "Invalid char '" << c << "'");
+ }
+}
+
+} // namespace ledger
diff --git a/token.h b/token.h
new file mode 100644
index 00000000..df08b6bc
--- /dev/null
+++ b/token.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2003-2008, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _TOKEN_H
+#define _TOKEN_H
+
+#include "expr.h"
+
+namespace ledger {
+
+struct expr_t::token_t : public noncopyable, public supports_flags<>
+{
+#define TOKEN_SHORT_ACCOUNT_MASK 0x01
+#define TOKEN_CODE_MASK 0x02
+#define TOKEN_COMMODITY_MASK 0x04
+#define TOKEN_PAYEE_MASK 0x08
+#define TOKEN_NOTE_MASK 0x10
+#define TOKEN_ACCOUNT_MASK 0x20
+
+ enum kind_t {
+ VALUE, // any kind of literal value
+ IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
+ MASK, // /regexp/
+
+ LPAREN, // (
+ RPAREN, // )
+
+ EQUAL, // ==
+ NEQUAL, // !=
+ LESS, // <
+ LESSEQ, // <=
+ GREATER, // >
+ GREATEREQ, // >=
+
+ ASSIGN, // =
+ MINUS, // -
+ PLUS, // +
+ STAR, // *
+ KW_DIV, // /
+
+ EXCLAM, // !
+ KW_AND, // &
+ KW_OR, // |
+ KW_MOD, // %
+
+ COMMA, // ,
+
+ TOK_EOF,
+ UNKNOWN
+
+ } kind;
+
+ char symbol[3];
+ value_t value;
+ std::size_t length;
+
+ explicit token_t() : supports_flags<>(), kind(UNKNOWN), length(0) {
+ TRACE_CTOR(token_t, "");
+ }
+ ~token_t() throw() {
+ TRACE_DTOR(token_t);
+ }
+
+ token_t& operator=(const token_t& other) {
+ if (&other == this)
+ return *this;
+ assert(false);
+ return *this;
+ }
+
+ void clear() {
+ kind = UNKNOWN;
+ length = 0;
+ value = NULL_VALUE;
+
+ symbol[0] = '\0';
+ symbol[1] = '\0';
+ symbol[2] = '\0';
+ }
+
+ void parse_ident(std::istream& in);
+ void next(std::istream& in, unsigned int flags);
+ void rewind(std::istream& in);
+ void unexpected();
+
+ static void unexpected(char c, char wanted = '\0');
+};
+
+} // namespace ledger
+
+#endif // _TOKEN_H
diff --git a/utils.h b/utils.h
index 51983d03..05a214a7 100644
--- a/utils.h
+++ b/utils.h
@@ -539,6 +539,80 @@ inline const string& either_or(const string& first,
return first.empty() ? second : first;
}
+inline char * skip_ws(char * ptr) {
+ while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
+ ptr++;
+ return ptr;
+}
+
+inline char * next_element(char * buf, bool variable = false) {
+ for (char * p = buf; *p; p++) {
+ if (! (*p == ' ' || *p == '\t'))
+ continue;
+
+ if (! variable) {
+ *p = '\0';
+ return skip_ws(p + 1);
+ }
+ else if (*p == '\t') {
+ *p = '\0';
+ return skip_ws(p + 1);
+ }
+ else if (*(p + 1) == ' ') {
+ *p = '\0';
+ return skip_ws(p + 2);
+ }
+ }
+ return NULL;
+}
+
+inline char peek_next_nonws(std::istream& in) {
+ char c = in.peek();
+ while (! in.eof() && std::isspace(c)) {
+ in.get(c);
+ c = in.peek();
+ }
+ return c;
+}
+
+#define READ_INTO(str, targ, size, var, cond) { \
+ char * _p = targ; \
+ var = str.peek(); \
+ while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
+ str.get(var); \
+ if (str.eof()) \
+ break; \
+ if (var == '\\') { \
+ str.get(var); \
+ if (in.eof()) \
+ break; \
+ } \
+ *_p++ = var; \
+ var = str.peek(); \
+ } \
+ *_p = '\0'; \
+}
+
+#define READ_INTO_(str, targ, size, var, idx, cond) { \
+ char * _p = targ; \
+ var = str.peek(); \
+ while (! str.eof() && var != '\n' && (cond) && _p - targ < size) { \
+ str.get(var); \
+ if (str.eof()) \
+ break; \
+ idx++; \
+ if (var == '\\') { \
+ str.get(var); \
+ if (in.eof()) \
+ break; \
+ idx++; \
+ } \
+ *_p++ = var; \
+ var = str.peek(); \
+ } \
+ *_p = '\0'; \
+}
+
} // namespace ledger
#endif // _UTILS_H
diff --git a/walk.cc b/walk.cc
index 40185543..b0aa31f6 100644
--- a/walk.cc
+++ b/walk.cc
@@ -14,6 +14,7 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
assert(left);
assert(right);
+#if 0
transaction_xdata_t& lxdata(transaction_xdata(*left));
if (! (lxdata.dflags & TRANSACTION_SORT_CALC)) {
sort_order.compute(lxdata.sort_value, details_t(*left));
@@ -34,6 +35,9 @@ bool compare_items<transaction_t>::operator()(const transaction_t * left,
"rxdata.sort_value = " << rxdata.sort_value);
return lxdata.sort_value < rxdata.sort_value;
+#else
+ return false;
+#endif
}
transaction_xdata_t& transaction_xdata(const transaction_t& xact)
@@ -379,7 +383,9 @@ void changed_value_transactions::output_diff(const datetime_t& current)
value_t cur_bal;
transaction_xdata(*last_xact).date = current;
+#if 0
compute_total(cur_bal, details_t(*last_xact));
+#endif
cur_bal.round();
// jww (2008-04-24): What does this do?
#if 0
@@ -413,7 +419,9 @@ void changed_value_transactions::operator()(transaction_t& xact)
item_handler<transaction_t>::operator()(xact);
+#if 0
compute_total(last_balance, details_t(xact));
+#endif
last_balance.round();
last_xact = &xact;
@@ -853,6 +861,7 @@ bool compare_items<account_t>::operator()(const account_t * left,
assert(left);
assert(right);
+#if 0
account_xdata_t& lxdata(account_xdata(*left));
if (! (lxdata.dflags & ACCOUNT_SORT_CALC)) {
sort_order.compute(lxdata.sort_value, details_t(*left));
@@ -866,6 +875,9 @@ bool compare_items<account_t>::operator()(const account_t * left,
}
return lxdata.sort_value < rxdata.sort_value;
+#else
+ return false;
+#endif
}
account_xdata_t& account_xdata(const account_t& account)
@@ -891,7 +903,9 @@ void sum_accounts(account_t& account)
}
value_t result;
+#if 0
compute_amount(result, details_t(account));
+#endif
if (! result.is_realzero())
xdata.total += result;
xdata.total_count += xdata.count;
diff --git a/walk.h b/walk.h
index 80879bf1..8d041f50 100644
--- a/walk.h
+++ b/walk.h
@@ -36,7 +36,7 @@ typedef shared_ptr<item_handler<transaction_t> > xact_handler_ptr;
template <typename T>
class compare_items
{
- value_expr sort_order;
+ expr_t sort_order;
compare_items();
@@ -44,7 +44,7 @@ public:
compare_items(const compare_items& other) : sort_order(other.sort_order) {
TRACE_CTOR(compare_items, "copy");
}
- compare_items(const value_expr& _sort_order) : sort_order(_sort_order) {
+ compare_items(const expr_t& _sort_order) : sort_order(_sort_order) {
TRACE_CTOR(compare_items, "const value_expr&");
}
~compare_items() throw() {
@@ -61,8 +61,10 @@ bool compare_items<T>::operator()(const T * left, const T * right)
value_t left_result;
value_t right_result;
+#if 0
sort_order.compute(left_result, details_t(*left));
sort_order.compute(right_result, details_t(*right));
+#endif
return left_result < right_result;
}
@@ -354,13 +356,13 @@ class sort_transactions : public item_handler<transaction_t>
typedef std::deque<transaction_t *> transactions_deque;
transactions_deque transactions;
- const value_expr sort_order;
+ const expr_t sort_order;
sort_transactions();
public:
sort_transactions(xact_handler_ptr handler,
- const value_expr& _sort_order)
+ const expr_t& _sort_order)
: item_handler<transaction_t>(handler),
sort_order(_sort_order) {
TRACE_CTOR(sort_transactions,
@@ -398,7 +400,7 @@ class sort_entries : public item_handler<transaction_t>
public:
sort_entries(xact_handler_ptr handler,
- const value_expr& _sort_order)
+ const expr_t& _sort_order)
: sorter(handler, _sort_order) {
TRACE_CTOR(sort_entries,
"xact_handler_ptr, const value_expr&");
@@ -436,7 +438,7 @@ class filter_transactions : public item_handler<transaction_t>
public:
filter_transactions(xact_handler_ptr handler,
- const value_expr& predicate)
+ const expr_t& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {
TRACE_CTOR(filter_transactions,
"xact_handler_ptr, const value_expr&");
@@ -540,7 +542,7 @@ class component_transactions : public item_handler<transaction_t>
public:
component_transactions(xact_handler_ptr handler,
- const value_expr& predicate)
+ const expr_t& predicate)
: item_handler<transaction_t>(handler), pred(predicate) {
TRACE_CTOR(component_transactions,
"xact_handler_ptr, const value_expr&");
@@ -883,9 +885,9 @@ class forecast_transactions : public generate_transactions
public:
forecast_transactions(xact_handler_ptr handler,
- const value_expr& predicate)
+ const expr_t& predicate)
: generate_transactions(handler), pred(predicate) {
- TRACE_CTOR(forecast_transactions, "xact_handler_ptr, const value_expr&");
+ TRACE_CTOR(forecast_transactions, "xact_handler_ptr, const expr_t&");
}
forecast_transactions(xact_handler_ptr handler,
const string& predicate)
@@ -972,7 +974,7 @@ public:
class sorted_accounts_iterator : public noncopyable
{
- value_expr sort_cmp;
+ expr_t sort_cmp;
typedef std::deque<account_t *> accounts_deque_t;
@@ -983,11 +985,11 @@ class sorted_accounts_iterator : public noncopyable
public:
sorted_accounts_iterator(const string& sort_order) {
TRACE_CTOR(sorted_accounts_iterator, "const string&");
- sort_cmp = value_expr(sort_order);
+ sort_cmp = expr_t(sort_order);
}
sorted_accounts_iterator(account_t& account, const string& sort_order) {
TRACE_CTOR(sorted_accounts_iterator, "account_t&, const string&");
- sort_cmp = value_expr(sort_order);
+ sort_cmp = expr_t(sort_order);
push_back(account);
}
virtual ~sorted_accounts_iterator() throw() {
diff --git a/xml.h b/xml.h
index ad2a19ff..fec25c85 100644
--- a/xml.h
+++ b/xml.h
@@ -1,14 +1,14 @@
#ifndef _XML_H
#define _XML_H
-#include "parser.h"
+#include "journal.h"
#include "format.h"
namespace ledger {
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
-class xml_parser_t : public parser_t
+class xml_parser_t : public journal_t::parser_t
{
public:
virtual bool test(std::istream& in) const;