summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2008-07-29 05:10:16 -0400
committerJohn Wiegley <johnw@newartisans.com>2008-07-29 05:59:38 -0400
commit4518ea95408e2d5fe90a87159b88bb41734ec1dc (patch)
tree3026169803aaea580d8b452cb9bb8a569c92fb2a
parent63039ade9209bced70b3e4dbb673ad90286d310a (diff)
downloadfork-ledger-4518ea95408e2d5fe90a87159b88bb41734ec1dc.tar.gz
fork-ledger-4518ea95408e2d5fe90a87159b88bb41734ec1dc.tar.bz2
fork-ledger-4518ea95408e2d5fe90a87159b88bb41734ec1dc.zip
Value expression architecture is now rewritten, but the functionality of the
old system (for example, the meaning of 'a') has yet to be restored. In the new scheme, this will be done by definition a function outside of the value expression logic, rather than the tight coupling between journal innards and value expressions that occurred in 2.x.
-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;