summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am48
-rw-r--r--config.cc12
-rw-r--r--main.py2
-rw-r--r--python.cc78
-rw-r--r--python.h55
-rw-r--r--sample.dat12
-rwxr-xr-xsetup.py26
-rwxr-xr-xsetup_amounts.py14
-rwxr-xr-xsetup_ledger.py16
-rw-r--r--textual.cc18
-rw-r--r--valexpr.cc60
-rw-r--r--valexpr.h4
-rw-r--r--value.cc24
13 files changed, 296 insertions, 73 deletions
diff --git a/Makefile.am b/Makefile.am
index 191017d2..098088ee 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,16 +1,18 @@
-lib_LTLIBRARIES = libamounts.la
-libamounts_la_SOURCES = amount.cc balance.cc value.cc
-libamounts_la_LDFLAGS = -version-info 2:0
-
-lib_LTLIBRARIES += libledger.la
-libledger_la_SOURCES = autoxact.cc binary.cc config.cc datetime.cc \
- format.cc journal.cc option.cc parser.cc qif.cc quotes.cc \
- textual.cc valexpr.cc walk.cc
+lib_LTLIBRARIES = libledger.la
+libledger_la_SOURCES = amount.cc balance.cc value.cc autoxact.cc \
+ binary.cc config.cc datetime.cc format.cc journal.cc option.cc \
+ parser.cc qif.cc quotes.cc textual.cc valexpr.cc walk.cc
if READ_GNUCASH
libledger_la_SOURCES += gnucash.cc
endif
+if HAVE_BOOST_PYTHON
+libledger_la_SOURCES += python.cc
+libledger_la_CXXFLAGS = -DUSE_BOOST_PYTHON=1
+else
+libledger_la_CXXFLAGS =
+endif
if DEBUG
-libledger_la_CXXFLAGS = -DDEBUG_LEVEL=4
+libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
libledger_la_SOURCES += debug.cc
endif
libledger_la_LDFLAGS = -version-info 2:0
@@ -22,7 +24,7 @@ if DEBUG
ledger_CXXFLAGS = -DDEBUG_LEVEL=4
endif
ledger_SOURCES = main.cc
-ledger_LDADD = $(LIBOBJS) libamounts.la libledger.la
+ledger_LDADD = $(LIBOBJS) libledger.la -lboost_python -L/sw/lib/python2.3/config -lpython2.3
nobase_include_HEADERS = \
amount.h \
@@ -48,17 +50,12 @@ info_TEXINFOS = ledger.texi
######################################################################
-if HAVE_PYTHON
if HAVE_BOOST_PYTHON
-noinst_LIBRARIES = libamounts_bpy.a
-libamounts_bpy_a_SOURCES = amount.cc balance.cc value.cc
-libamounts_bpy_a_CXXFLAGS = -DUSE_BOOST_PYTHON=1
-
-noinst_LIBRARIES += libledger_bpy.a
-libledger_bpy_a_SOURCES = autoxact.cc binary.cc config.cc datetime.cc \
- format.cc journal.cc option.cc parser.cc qif.cc quotes.cc \
- textual.cc valexpr.cc walk.cc
+noinst_LIBRARIES = libledger_bpy.a
+libledger_bpy_a_SOURCES = amount.cc balance.cc value.cc autoxact.cc \
+ binary.cc config.cc datetime.cc format.cc journal.cc option.cc \
+ parser.cc qif.cc quotes.cc textual.cc valexpr.cc walk.cc python.cc
libledger_bpy_a_CXXFLAGS = -DUSE_BOOST_PYTHON=1
if READ_GNUCASH
libledger_bpy_a_SOURCES += gnucash.cc
@@ -68,17 +65,12 @@ libledger_bpy_a_CXXFLAGS += -DDEBUG_LEVEL=4
libledger_bpy_a_SOURCES += debug.cc
endif
-bin_PROGRAMS += amounts.so ledger.so
-
-amounts.so: pyamounts.cc libamounts_bpy.a
- CFLAGS="$(CPPFLAGS) -L." python setup_amounts.py build --build-lib=.
+bin_PROGRAMS += ledger.so
-ledger.so: pyledger.cc libamounts_bpy.a libledger_bpy.a
- CFLAGS="$(CPPFLAGS) -L." python setup_ledger.py build --build-lib=.
+ledger.so: pyledger.cc libledger_bpy.a
+ CFLAGS="$(CPPFLAGS) -L." python setup.py build --build-lib=.
install-exec-hook:
- CFLAGS="$(CPPFLAGS) -L." python setup_amounts.py install --prefix=$(DESTDIR)
- CFLAGS="$(CPPFLAGS) -L." python setup_ledger.py install --prefix=$(DESTDIR)
+ CFLAGS="$(CPPFLAGS) -L." python setup.py install --prefix=$(DESTDIR)
endif
-endif
diff --git a/config.cc b/config.cc
index 90ea4893..7a249c2d 100644
--- a/config.cc
+++ b/config.cc
@@ -328,8 +328,8 @@ Output customization:\n\
-Y, --yearly show yearly sub-totals\n\
-l, --limit EXPR calculate only transactions matching EXPR\n\
-d, --display EXPR display only transactions matching EXPR\n\
- -t, --value EXPR set the value expression for all report types\n\
- -T, --total EXPR set the total expression for all report types\n\
+ -t, --value-expr EXPR set the value expression for all report types\n\
+ -T, --total-expr EXPR set the total expression for all report types\n\
-j, --value-data print only raw value data (useful when scripting)\n\
-J, --total-data print only raw total data\n\n\
Commodity reporting:\n\
@@ -531,13 +531,13 @@ OPT_BEGIN(display, "d:") {
config.display_predicate += ")";
} OPT_END(display);
-OPT_BEGIN(value, "t:") {
+OPT_BEGIN(value_expr, "t:") {
config.value_expr = optarg;
-} OPT_END(value);
+} OPT_END(value_expr);
-OPT_BEGIN(total, "T:") {
+OPT_BEGIN(total_expr, "T:") {
config.total_expr = optarg;
-} OPT_END(total);
+} OPT_END(total_expr);
OPT_BEGIN(value_data, "j") {
config.value_expr = "S" + config.value_expr;
diff --git a/main.py b/main.py
index e3f266a3..7cf02c2b 100644
--- a/main.py
+++ b/main.py
@@ -28,7 +28,7 @@ class FormatTransaction (TransactionHandler):
def __call__ (self, xact):
print self.formatter.format(xact)
-handler = FormatTransaction("%D %-20P %N")
+handler = FormatTransaction("%D %-20P %N %('foo'100)")
handler = FilterTransactions (handler, "/Checking/")
expr = parse_value_expr ("a*2")
diff --git a/python.cc b/python.cc
new file mode 100644
index 00000000..9699873c
--- /dev/null
+++ b/python.cc
@@ -0,0 +1,78 @@
+#include "python.h"
+#include "ledger.h"
+#include "acconf.h"
+
+#include <boost/python.hpp>
+
+using namespace boost::python;
+
+void export_amount();
+void export_balance();
+void export_value();
+void export_journal();
+void export_parser();
+void export_textual();
+void export_binary();
+void export_qif();
+#ifdef READ_GNUCASH
+void export_gnucash();
+#endif
+void export_option();
+void export_walk();
+void export_format();
+void export_valexpr();
+void export_datetime();
+
+namespace ledger {
+
+python_support * python_interpretor = NULL;
+
+static struct cleanup_python {
+ ~cleanup_python() {
+ if (python_interpretor) {
+ Py_Finalize();
+ delete python_interpretor;
+ }
+ }
+} _cleanup;
+
+void init_module()
+{
+ export_amount();
+ export_balance();
+ export_value();
+ export_journal();
+ export_parser();
+ export_textual();
+ export_binary();
+ export_qif();
+#ifdef READ_GNUCASH
+ export_gnucash();
+#endif
+ export_option();
+ export_walk();
+ export_format();
+ export_valexpr();
+ export_datetime();
+}
+
+void init_python()
+{
+ assert(! python_interpretor);
+
+ Py_Initialize();
+ python_interpretor = new python_support;
+
+#if 1
+ boost::python::detail::init_module("ledger", &init_module);
+#else
+ object m_obj(python_interpretor->main_module);
+ scope current_module(m_obj);
+
+ python_interpretor->main_namespace.attr("bar") = 10;
+
+ handle_exception(init_module_main);
+#endif
+}
+
+} // namespace ledger
diff --git a/python.h b/python.h
new file mode 100644
index 00000000..7faefa0d
--- /dev/null
+++ b/python.h
@@ -0,0 +1,55 @@
+#ifndef _PYTHON_H
+#define _PYTHON_H
+
+#include <boost/python.hpp>
+
+using namespace boost::python;
+
+namespace ledger {
+
+struct python_support
+{
+ handle<> main_module;
+ dict main_namespace;
+
+ python_support()
+ : main_module(borrowed(PyImport_AddModule("__main__"))),
+ main_namespace(handle<>(borrowed(PyModule_GetDict(main_module.get()))))
+ {}
+ ~python_support() {
+ }
+};
+
+extern python_support * python_interpretor;
+
+void init_python();
+
+inline void python_eval(std::istream& in)
+{
+ if (! python_interpretor)
+ init_python();
+
+ std::string buffer;
+ buffer.reserve(4096);
+ while (! in.eof()) {
+ char buf[256];
+ in.getline(buf, 255);
+ if (buf[0] == '!')
+ break;
+ buffer += buf;
+ buffer += "\n";
+ }
+
+ try {
+ handle<>(borrowed(PyRun_String(buffer.c_str(), Py_file_input,
+ python_interpretor->main_namespace.ptr(),
+ python_interpretor->main_namespace.ptr())));
+ }
+ catch(const boost::python::error_already_set&) {
+ PyErr_Print();
+ }
+}
+
+} // namespace ledger
+
+#endif // _PYTHON_H
diff --git a/sample.dat b/sample.dat
index 335083d8..69379382 100644
--- a/sample.dat
+++ b/sample.dat
@@ -1,3 +1,14 @@
+;; If Python support is enabled, then the following code may be
+;; uncomment to demonstrate a rather useless example.
+
+; !python
+; from ledger import *
+; def foo(d, val):
+; return val + d.xact.amount
+; !end
+;
+; --value-expr 'foo'{$100}
+
2004/05/01 Checking balance
Assets:Checking $500.00
Equity:Opening Balances
@@ -9,4 +20,3 @@
2004/05/29 Restaurant
Expenses:Food $50.00
Liabilities:MasterCard
-
diff --git a/setup.py b/setup.py
new file mode 100755
index 00000000..5cb1e67e
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+setup(name = "Amounts",
+ version = "2.0b",
+ description = "Amounts Library",
+ author = "John Wiegley",
+ author_email = "johnw@newartisans.com",
+ url = "http://www.newartisans.com/johnw/",
+ ext_modules = [
+ Extension("amounts", ["pyamounts.cc"],
+ define_macros = [('PYTHON_MODULE', None)],
+ libraries = ["ledger_bpy", "boost_python", "gmp"])])
+
+setup(name = "Ledger",
+ version = "2.0b",
+ description = "Ledger Accounting Tool",
+ author = "John Wiegley",
+ author_email = "johnw@newartisans.com",
+ url = "http://www.newartisans.com/johnw/",
+ ext_modules = [
+ Extension("ledger", ["pyledger.cc"],
+ define_macros = [('PYTHON_MODULE', None)],
+ libraries = ["ledger_bpy", "boost_python", "gmp",
+ "pcre", "xmlparse", "xmltok"])])
diff --git a/setup_amounts.py b/setup_amounts.py
deleted file mode 100755
index 663faa2e..00000000
--- a/setup_amounts.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-
-from distutils.core import setup, Extension
-
-setup(name = "Amounts",
- version = "2.0b",
- description = "Amounts Library",
- author = "John Wiegley",
- author_email = "johnw@newartisans.com",
- url = "http://www.newartisans.com/johnw/",
- ext_modules = [
- Extension("amounts", ["pyamounts.cc"],
- define_macros = [('PYTHON_MODULE', None)],
- libraries = ["amounts_bpy", "boost_python", "gmp"])])
diff --git a/setup_ledger.py b/setup_ledger.py
deleted file mode 100755
index 71d8f56a..00000000
--- a/setup_ledger.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/usr/bin/env python
-
-from distutils.core import setup, Extension
-
-setup(name = "Ledger",
- version = "2.0b",
- description = "Ledger Accounting Tool",
- author = "John Wiegley",
- author_email = "johnw@newartisans.com",
- url = "http://www.newartisans.com/johnw/",
- ext_modules = [
- Extension("ledger", ["pyledger.cc"],
- define_macros = [('PYTHON_MODULE', None)],
- libraries = ["amounts_bpy", "gmp",
- "ledger_bpy", "boost_python",
- "pcre", "xmlparse", "xmltok"])])
diff --git a/textual.cc b/textual.cc
index 53310a63..6e80884b 100644
--- a/textual.cc
+++ b/textual.cc
@@ -7,6 +7,9 @@
#include "option.h"
#include "timing.h"
#include "util.h"
+#ifdef USE_BOOST_PYTHON
+#include "python.h"
+#endif
#include <fstream>
#include <sstream>
@@ -534,9 +537,11 @@ unsigned int textual_parser_t::parse(std::istream& in,
break;
}
- case '!': // directive
- in >> line;
- if (std::string(line) == "!include") {
+ case '!': { // directive
+ std::string word;
+ in.get(c);
+ in >> word;
+ if (word == "include") {
in.getline(line, MAX_LINE);
linenum++;
@@ -545,7 +550,14 @@ unsigned int textual_parser_t::parse(std::istream& in,
count += parse_journal_file(skip_ws(line), journal,
account_stack.front());
}
+#ifdef USE_BOOST_PYTHON
+ else if (word == "python") {
+ in.getline(line, MAX_LINE);
+ python_eval(in);
+ }
+#endif
break;
+ }
default: {
unsigned int first_line = linenum;
diff --git a/valexpr.cc b/valexpr.cc
index f7763084..8b6b5d7d 100644
--- a/valexpr.cc
+++ b/valexpr.cc
@@ -5,6 +5,9 @@
#include "datetime.h"
#include "debug.h"
#include "util.h"
+#ifdef USE_BOOST_PYTHON
+#include "python.h"
+#endif
#include <pcre.h>
@@ -277,6 +280,27 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
}
+ case F_INTERP_FUNC: {
+#ifdef USE_BOOST_PYTHON
+ if (! python_interpretor)
+ init_python();
+
+ try {
+ object func = python_interpretor->main_namespace[constant_s];
+ if (right) {
+ right->compute(result, details);
+ result = call<value_t>(func.ptr(), details, result);
+ } else {
+ result = call<value_t>(func.ptr(), details);
+ }
+ }
+ catch(const boost::python::error_already_set&) {
+ PyErr_Print();
+ }
+#endif
+ break;
+ }
+
case O_NOT:
left->compute(result, details);
result.negate();
@@ -489,13 +513,25 @@ value_expr_t * parse_value_term(std::istream& in)
in.get(c);
node.reset(new value_expr_t(short_account_mask ?
- value_expr_t::F_SHORT_ACCOUNT_MASK :
- (payee_mask ? value_expr_t::F_PAYEE_MASK :
- value_expr_t::F_ACCOUNT_MASK)));
+ value_expr_t::F_SHORT_ACCOUNT_MASK :
+ (payee_mask ? value_expr_t::F_PAYEE_MASK :
+ value_expr_t::F_ACCOUNT_MASK)));
node->mask = new mask_t(buf);
break;
}
+ case '\'': {
+ READ_INTO(in, buf, 255, c, c != '\'');
+ if (c != '\'')
+ throw value_expr_error("Missing closing '\''");
+
+ in.get(c);
+ node.reset(new value_expr_t(value_expr_t::F_INTERP_FUNC));
+ node->constant_s = buf;
+ node->right = parse_value_expr(in);
+ break;
+ }
+
case '(':
node.reset(parse_value_expr(in));
if (peek_next_nonws(in) == ')')
@@ -781,6 +817,10 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
out << ")";
break;
+ case value_expr_t::F_INTERP_FUNC:
+ out << "F_INTERP(" << node->constant_s << ")";
+ break;
+
case value_expr_t::O_NOT:
out << "!";
dump_value_expr(out, node->left);
@@ -874,6 +914,20 @@ value_expr_t * py_parse_value_expr(const std::string& str) {
void export_valexpr()
{
+ class_< details_t > ("Details", init<const entry_t&>())
+ .def(init<const transaction_t&>())
+ .def(init<const account_t&>())
+ .add_property("entry",
+ make_getter(&details_t::entry,
+ return_value_policy<reference_existing_object>()))
+ .add_property("xact",
+ make_getter(&details_t::xact,
+ return_value_policy<reference_existing_object>()))
+ .add_property("account",
+ make_getter(&details_t::account,
+ return_value_policy<reference_existing_object>()))
+ ;
+
class_< value_expr_t > ("ValueExpr", init<value_expr_t::kind_t>())
.def("compute", py_compute<account_t>)
.def("compute", py_compute<entry_t>)
diff --git a/valexpr.h b/valexpr.h
index 91de7ed7..29333092 100644
--- a/valexpr.h
+++ b/valexpr.h
@@ -72,6 +72,7 @@ struct value_expr_t
F_PAYEE_MASK,
F_ACCOUNT_MASK,
F_SHORT_ACCOUNT_MASK,
+ F_INTERP_FUNC,
// Binary operators
O_ADD,
@@ -100,7 +101,8 @@ struct value_expr_t
std::time_t constant_t;
unsigned int constant_i;
};
- amount_t constant_a;
+ std::string constant_s;
+ amount_t constant_a;
mask_t * mask;
value_expr_t(const kind_t _kind)
diff --git a/value.cc b/value.cc
index fe8c5264..2a211fd1 100644
--- a/value.cc
+++ b/value.cc
@@ -618,6 +618,30 @@ void export_value()
.def(init<unsigned int>())
.def(init<bool>())
+ .def(self + self)
+ .def(self + other<balance_pair_t>())
+ .def(self + other<balance_t>())
+ .def(self + other<amount_t>())
+ .def(self + int())
+
+ .def(self - self)
+ .def(self - other<balance_pair_t>())
+ .def(self - other<balance_t>())
+ .def(self - other<amount_t>())
+ .def(self - int())
+
+ .def(self * self)
+ .def(self * other<balance_pair_t>())
+ .def(self * other<balance_t>())
+ .def(self * other<amount_t>())
+ .def(self * int())
+
+ .def(self / self)
+ .def(self / other<balance_pair_t>())
+ .def(self / other<balance_t>())
+ .def(self / other<amount_t>())
+ .def(self / int())
+
.def(self += self)
.def(self += other<balance_pair_t>())
.def(self += other<balance_t>())