From e2afc783db0dff1927b00dc506390353d9e3bbd2 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 29 Feb 2012 22:32:23 -0600 Subject: Increased file copyrights to 2012 --- src/pyinterp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/pyinterp.h') diff --git a/src/pyinterp.h b/src/pyinterp.h index ea947c5a..ae8dd9c2 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * Copyright (c) 2003-2012, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are -- cgit v1.2.3 From f6c087cfe48e6410db61a9367ce7c718a490af77 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 1 Mar 2012 17:32:51 -0600 Subject: Added a new 'python' directive --- src/py_value.cc | 2 +- src/pyinterp.cc | 77 +++++++++++++++++++++++++++------------ src/pyinterp.h | 5 ++- src/textual.cc | 62 +++++++++++++++++++++++++++++++ test/LedgerHarness.py | 4 ++ test/RegressTests.py | 4 +- test/baseline/dir-python_py.test | 26 +++++++++++++ test/baseline/feat-import_py.test | 23 ++++++++++++ test/baseline/featimport.py | 4 ++ tools/Makefile.am | 16 +++++--- 10 files changed, 190 insertions(+), 33 deletions(-) create mode 100644 test/baseline/dir-python_py.test create mode 100644 test/baseline/feat-import_py.test create mode 100644 test/baseline/featimport.py (limited to 'src/pyinterp.h') diff --git a/src/py_value.cc b/src/py_value.cc index 3b67c4c6..949f2a49 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -147,7 +147,7 @@ void export_value() .def(init()) .def(init()) .def(init()) - // jww (2009-11-02): Need to support conversion eof value_t::sequence_t + // jww (2009-11-02): Need to support conversion of value_t::sequence_t //.def(init()) .def(init()) diff --git a/src/pyinterp.cc b/src/pyinterp.cc index adcd0167..dc6fb4f7 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -32,6 +32,7 @@ #include #include "pyinterp.h" +#include "pyutils.h" #include "account.h" #include "xact.h" #include "post.h" @@ -103,7 +104,7 @@ void python_interpreter_t::initialize() hack_system_paths(); - object main_module = python::import("__main__"); + main_module = python::import("__main__"); if (! main_module) throw_(std::runtime_error, _("Python failed to initialize (couldn't find __main__)")); @@ -446,28 +447,56 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, } namespace { - void append_value(list& lst, const value_t& value) + object convert_value_to_python(const value_t& val) { - if (value.is_scope()) { - const scope_t * scope = value.as_scope(); - if (const post_t * post = dynamic_cast(scope)) - lst.append(ptr(post)); - else if (const xact_t * xact = dynamic_cast(scope)) - lst.append(ptr(xact)); - else if (const account_t * account = - dynamic_cast(scope)) - lst.append(ptr(account)); - else if (const period_xact_t * period_xact = - dynamic_cast(scope)) - lst.append(ptr(period_xact)); - else if (const auto_xact_t * auto_xact = - dynamic_cast(scope)) - lst.append(ptr(auto_xact)); - else - throw_(std::logic_error, - _("Cannot downcast scoped object to specific type")); - } else { - lst.append(value); + switch (val.type()) { + case value_t::VOID: // a null value (i.e., uninitialized) + return object(); + case value_t::BOOLEAN: // a boolean + return object(val.to_boolean()); + case value_t::DATETIME: // a date and time (Boost posix_time) + return object(val.to_datetime()); + case value_t::DATE: // a date (Boost gregorian::date) + return object(val.to_date()); + case value_t::INTEGER: // a signed integer value + return object(val.to_long()); + case value_t::AMOUNT: // a ledger::amount_t + return object(val.as_amount()); + case value_t::BALANCE: // a ledger::balance_t + return object(val.as_balance()); + case value_t::STRING: // a string object + return object(handle<>(borrowed(str_to_py_unicode(val.as_string())))); + case value_t::MASK: // a regular expression mask + return object(handle<>(borrowed(str_to_py_unicode(val.as_mask().str())))); + case value_t::SEQUENCE: { // a vector of value_t objects + list arglist; + foreach (const value_t& elem, val.as_sequence()) + arglist.append(elem); + return arglist; + } + case value_t::SCOPE: // a pointer to a scope + if (const scope_t * scope = val.as_scope()) { + if (const post_t * post = dynamic_cast(scope)) + return object(ptr(post)); + else if (const xact_t * xact = dynamic_cast(scope)) + return object(ptr(xact)); + else if (const account_t * account = + dynamic_cast(scope)) + return object(ptr(account)); + else if (const period_xact_t * period_xact = + dynamic_cast(scope)) + return object(ptr(period_xact)); + else if (const auto_xact_t * auto_xact = + dynamic_cast(scope)) + return object(ptr(auto_xact)); + else + throw_(std::logic_error, + _("Cannot downcast scoped object to specific type")); + } + return object(); + case value_t::ANY: // a pointer to an arbitrary object + assert("Attempted to convert an Value.ANY object to Python" == NULL); + return object(); } } } @@ -491,9 +520,9 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args) // rather than a sequence of arguments? if (args.value().is_sequence()) foreach (const value_t& value, args.value().as_sequence()) - append_value(arglist, value); + arglist.append(convert_value_to_python(value)); else - append_value(arglist, args.value()); + arglist.append(convert_value_to_python(args.value())); if (PyObject * val = PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) { diff --git a/src/pyinterp.h b/src/pyinterp.h index ae8dd9c2..c3397840 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -41,8 +41,9 @@ namespace ledger { class python_interpreter_t : public session_t { public: - python::dict main_nspace; - bool is_initialized; + python::object main_module; + python::dict main_nspace; + bool is_initialized; python_interpreter_t() : session_t(), main_nspace(), is_initialized(false) { diff --git a/src/textual.cc b/src/textual.cc index 4a384866..51b5849b 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -40,6 +40,9 @@ #include "query.h" #include "pstream.h" #include "pool.h" +#if defined(HAVE_BOOST_PYTHON) +#include "pyinterp.h" +#endif #define TIMELOG_SUPPORT 1 #if defined(TIMELOG_SUPPORT) @@ -104,6 +107,10 @@ namespace { return (in.good() && ! in.eof() && (in.peek() == ' ' || in.peek() == '\t')); } + bool peek_blank_line() { + return (in.good() && ! in.eof() && + (in.peek() == '\n' || in.peek() == '\r')); + } void read_next_directive(); @@ -157,6 +164,10 @@ namespace { void assert_directive(char * line); void check_directive(char * line); +#if defined(HAVE_BOOST_PYTHON) + void python_directive(char * line); +#endif + post_t * parse_post(char * line, std::streamsize len, account_t * account, @@ -1094,6 +1105,48 @@ void instance_t::comment_directive(char * line) } } +#if defined(HAVE_BOOST_PYTHON) +void instance_t::python_directive(char * line) +{ + std::ostringstream script; + + if (line) + script << skip_ws(line) << '\n'; + + std::size_t indent = 0; + + while (peek_whitespace_line() || peek_blank_line()) { + if (read_line(line) > 0) { + if (! indent) { + const char * p = line; + while (*p && std::isspace(*p)) { + ++indent; + ++p; + } + } + + const char * p = line; + for (std::size_t i = 0; i < indent; i++) { + if (std::isspace(*p)) + ++p; + else + break; + } + + if (*p) + script << p << '\n'; + } + } + + if (! python_session->is_initialized) + python_session->initialize(); + + python_session->main_nspace["journal"] = + python::object(python::ptr(context.journal)); + python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); +} +#endif // HAVE_BOOST_PYTHON + bool instance_t::general_directive(char * line) { char buf[8192]; @@ -1178,6 +1231,15 @@ bool instance_t::general_directive(char * line) payee_directive(arg); return true; } + else if (std::strcmp(p, "python") == 0) { +#if defined(HAVE_BOOST_PYTHON) + python_directive(arg); +#else + throw_(parse_error, + _("'python' directive seen, but Python support is missing")); +#endif + return true; + } break; case 't': diff --git a/test/LedgerHarness.py b/test/LedgerHarness.py index c0dbe368..7b4dfa83 100755 --- a/test/LedgerHarness.py +++ b/test/LedgerHarness.py @@ -34,6 +34,7 @@ class LedgerHarness: failed = 0 verify = False gmalloc = False + python = False def __init__(self, argv): if not os.path.isfile(argv[1]): @@ -49,6 +50,9 @@ class LedgerHarness: self.failed = 0 self.verify = '--verify' in argv self.gmalloc = '--gmalloc' in argv + self.python = '--python' in argv + + os.chdir(self.sourcepath) def run(self, command, verify=None, gmalloc=None, columns=True): env = os.environ.copy() diff --git a/test/RegressTests.py b/test/RegressTests.py index 28a6c709..def202e4 100755 --- a/test/RegressTests.py +++ b/test/RegressTests.py @@ -179,7 +179,9 @@ if __name__ == '__main__': if os.path.isdir(tests): tests = [os.path.join(tests, x) - for x in os.listdir(tests) if x.endswith('.test')] + for x in os.listdir(tests) + if (x.endswith('.test') and + (not '_py.test' in x or harness.python))] if pool: pool.map(do_test, tests, 1) else: diff --git a/test/baseline/dir-python_py.test b/test/baseline/dir-python_py.test new file mode 100644 index 00000000..e4681075 --- /dev/null +++ b/test/baseline/dir-python_py.test @@ -0,0 +1,26 @@ +python + import os + def check_path(path_value): + return os.path.isfile(path_value) + +tag PATH + check check_path(value) + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_py.test + Expenses:Food $20 + Assets:Cash + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_noexist.test + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +__ERROR__ +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 17: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +end test diff --git a/test/baseline/feat-import_py.test b/test/baseline/feat-import_py.test new file mode 100644 index 00000000..6bd77586 --- /dev/null +++ b/test/baseline/feat-import_py.test @@ -0,0 +1,23 @@ +--import featimport.py + +tag PATH + check check_path(value) + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_py.test + Expenses:Food $20 + Assets:Cash + +2012-02-29 KFC + ; PATH: test/baseline/feat-import_noexist.test + Expenses:Food $20 + Assets:Cash + +test reg +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +12-Feb-29 KFC Expenses:Food $20 $20 + Assets:Cash $-20 0 +__ERROR__ +Warning: "$sourcepath/test/baseline/feat-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +end test diff --git a/test/baseline/featimport.py b/test/baseline/featimport.py new file mode 100644 index 00000000..9edd9ba3 --- /dev/null +++ b/test/baseline/featimport.py @@ -0,0 +1,4 @@ +import os + +def check_path(path_value): + return os.path.isfile(str(path_value)) diff --git a/tools/Makefile.am b/tools/Makefile.am index 4fdd8393..bff1af1a 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -358,8 +358,14 @@ RegressTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/regress test/convert.py test/LedgerHarness.py +if HAVE_BOOST_PYTHON +TEST_PYTHON_FLAGS = --python +else +TEST_PYTHON_FLAGS = +endif + RegressTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ BaselineTests_SOURCES = test/RegressTests.py @@ -367,7 +373,7 @@ BaselineTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/baseline BaselineTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ ManualTests_SOURCES = test/RegressTests.py @@ -375,7 +381,7 @@ ManualTests_SOURCES = test/RegressTests.py EXTRA_DIST += test/manual ManualTests: $(srcdir)/test/RegressTests.py - echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ ConfirmTests_SOURCES = test/ConfirmTests.py @@ -390,13 +396,13 @@ test/input/mondo.dat: test/input/standard.dat done ConfirmTests: $(srcdir)/test/ConfirmTests.py - echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ GenerateTests_SOURCES = test/GenerateTests.py GenerateTests: $(srcdir)/test/GenerateTests.py - echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} \"\$$@\"" > $@ + echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@ chmod 755 $@ CheckTests_SOURCES = test/CheckTests.py -- cgit v1.2.3 From ddba59b703fcfeb546627ee7e44a25fab49c0c12 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 2 Mar 2012 01:36:58 -0600 Subject: This now works: ledger --import os eval 'os.path.isdir("/tmp")' --- src/pyinterp.cc | 143 +++++++++++++++++++++++---------------- src/pyinterp.h | 58 +++++++++++----- src/textual.cc | 4 +- test/baseline/dir-python_py.test | 8 ++- 4 files changed, 135 insertions(+), 78 deletions(-) (limited to 'src/pyinterp.h') diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 3157079a..d733c40d 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -84,16 +84,56 @@ struct python_run python_run(python_interpreter_t * intepreter, const string& str, int input_mode) - : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode, - intepreter->main_nspace.ptr(), - intepreter->main_nspace.ptr())))) {} + : result + (handle<> + (borrowed + (PyRun_String(str.c_str(), input_mode, + intepreter->main_module->module_globals.ptr(), + intepreter->main_module->module_globals.ptr())))) {} operator object() { return result; } }; +python_module_t::python_module_t(const string& name) + : scope_t(), module_name(name), module_globals() +{ + import_module(name); +} + +python_module_t::python_module_t(const string& name, python::object obj) + : scope_t(), module_name(name), module_globals() +{ + module_object = obj; + module_globals = extract(module_object.attr("__dict__")); +} + +void python_module_t::import_module(const string& name, bool import_direct) +{ + object mod = python::import(name.c_str()); + if (! mod) + throw_(std::runtime_error, + _("Module import failed (couldn't find %1)") << name); + + dict globals = extract(mod.attr("__dict__")); + if (! globals) + throw_(std::runtime_error, + _("Module import failed (couldn't find %1)") << name); + + if (! import_direct) { + module_object = mod; + module_globals = globals; + } else { + // Import all top-level entries directly into the namespace + module_globals.update(mod.attr("__dict__")); + } +} + void python_interpreter_t::initialize() { + if (is_initialized) + return; + TRACE_START(python_init, 1, "Initialized Python"); try { @@ -104,15 +144,7 @@ void python_interpreter_t::initialize() hack_system_paths(); - main_module = python::import("__main__"); - if (! main_module) - throw_(std::runtime_error, - _("Python failed to initialize (couldn't find __main__)")); - - main_nspace = extract(main_module.attr("__dict__")); - if (! main_nspace) - throw_(std::runtime_error, - _("Python failed to initialize (couldn't find __dict__)")); + main_module = import_module("__main__"); python::detail::init_module("ledger", &initialize_for_python); @@ -170,28 +202,6 @@ void python_interpreter_t::hack_system_paths() #endif } -object python_interpreter_t::import_into_main(const string& str) -{ - if (! is_initialized) - initialize(); - - try { - object mod = python::import(str.c_str()); - if (! mod) - throw_(std::runtime_error, - _("Failed to import Python module %1") << str); - - // Import all top-level entries directly into the main namespace - main_nspace.update(mod.attr("__dict__")); - - return mod; - } - catch (const error_already_set&) { - PyErr_Print(); - } - return object(); -} - object python_interpreter_t::import_option(const string& str) { if (! is_initialized) @@ -229,13 +239,10 @@ object python_interpreter_t::import_option(const string& str) } try { - if (contains(str, ".py")) { - import_into_main(name); - } else { - object obj = python::import(python::str(name.c_str())); - main_nspace[name.c_str()] = obj; - return obj; - } + if (contains(str, ".py")) + main_module->import_module(name, true); + else + import_module(str); } catch (const error_already_set&) { PyErr_Print(); @@ -399,6 +406,40 @@ python_interpreter_t::lookup_option(const char * p) return NULL; } +expr_t::ptr_op_t python_module_t::lookup(const symbol_t::kind_t kind, + const string& name) +{ + switch (kind) { + case symbol_t::FUNCTION: + DEBUG("python.interp", "Python lookup: " << name); + if (module_globals.has_key(name.c_str())) { + if (python::object obj = module_globals.get(name.c_str())) { + if (PyModule_Check(obj.ptr())) { + shared_ptr mod; + python_module_map_t::iterator i = + python_session->modules_map.find(obj.ptr()); + if (i == python_session->modules_map.end()) { + mod.reset(new python_module_t(name, obj)); + python_session->modules_map.insert + (python_module_map_t::value_type(obj.ptr(), mod)); + } else { + mod = (*i).second; + } + return expr_t::op_t::wrap_value(scope_value(mod.get())); + } else { + return WRAP_FUNCTOR(python_interpreter_t::functor_t(obj, name)); + } + } + } + break; + + default: + break; + } + + return NULL; +} + expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, const string& name) { @@ -408,28 +449,16 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind, switch (kind) { case symbol_t::FUNCTION: - if (option_t * handler = lookup_option(name.c_str())) - return MAKE_OPT_FUNCTOR(python_interpreter_t, handler); - - if (is_initialized && main_nspace.has_key(name.c_str())) { - DEBUG("python.interp", "Python lookup: " << name); - - if (python::object obj = main_nspace.get(name.c_str())) - return WRAP_FUNCTOR(functor_t(obj, name)); - } + if (is_initialized) + return main_module->lookup(kind, name); break; case symbol_t::OPTION: { if (option_t * handler = lookup_option(name.c_str())) return MAKE_OPT_HANDLER(python_interpreter_t, handler); - string option_name(string("option_") + name); - if (is_initialized && main_nspace.has_key(option_name.c_str())) { - DEBUG("python.interp", "Python lookup option: " << option_name); - - if (python::object obj = main_nspace.get(option_name.c_str())) - return WRAP_FUNCTOR(functor_t(obj, option_name)); - } + if (is_initialized) + return main_module->lookup(symbol_t::FUNCTION, string("option_") + name); break; } diff --git a/src/pyinterp.h b/src/pyinterp.h index c3397840..8699f69d 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -38,21 +38,52 @@ namespace ledger { +class python_module_t : public scope_t, public noncopyable +{ +public: + string module_name; + python::object module_object; + python::dict module_globals; + + explicit python_module_t(const string& name); + explicit python_module_t(const string& name, python::object obj); + + void import_module(const string& name, bool import_direct = false); + + virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, + const string& name); + + void define_global(const string& name, python::object obj) { + module_globals[name] = obj; + } + + virtual string description() { + return module_name; + } +}; + +typedef std::map > python_module_map_t; + class python_interpreter_t : public session_t { public: - python::object main_module; - python::dict main_nspace; - bool is_initialized; + bool is_initialized; - python_interpreter_t() - : session_t(), main_nspace(), is_initialized(false) { + shared_ptr main_module; + python_module_map_t modules_map; + + shared_ptr import_module(const string& name) { + shared_ptr mod(new python_module_t(name)); + if (name != "__main__") + main_module->define_global(name, mod->module_object); + return mod; + } + + python_interpreter_t() : session_t(), is_initialized(false) { TRACE_CTOR(python_interpreter_t, ""); } - virtual ~python_interpreter_t() { TRACE_DTOR(python_interpreter_t); - if (is_initialized) Py_Finalize(); } @@ -60,7 +91,6 @@ public: void initialize(); void hack_system_paths(); - python::object import_into_main(const string& name); python::object import_option(const string& name); enum py_eval_mode_t { @@ -69,14 +99,10 @@ public: PY_EVAL_MULTI }; - python::object eval(std::istream& in, - py_eval_mode_t mode = PY_EVAL_EXPR); - python::object eval(const string& str, - py_eval_mode_t mode = PY_EVAL_EXPR); - python::object eval(const char * c_str, - py_eval_mode_t mode = PY_EVAL_EXPR) { - string str(c_str); - return eval(str, mode); + python::object eval(std::istream& in, py_eval_mode_t mode = PY_EVAL_EXPR); + python::object eval(const string& str, py_eval_mode_t mode = PY_EVAL_EXPR); + python::object eval(const char * c_str, py_eval_mode_t mode = PY_EVAL_EXPR) { + return eval(string(c_str), mode); } value_t python_command(call_scope_t& scope); diff --git a/src/textual.cc b/src/textual.cc index a4e03435..95635184 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1156,8 +1156,8 @@ void instance_t::python_directive(char * line) if (! python_session->is_initialized) python_session->initialize(); - python_session->main_nspace["journal"] = - python::object(python::ptr(context.journal)); + python_session->main_module->define_global + ("journal", python::object(python::ptr(context.journal))); python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI); } #endif // HAVE_BOOST_PYTHON diff --git a/test/baseline/dir-python_py.test b/test/baseline/dir-python_py.test index e4681075..99ff4b1b 100644 --- a/test/baseline/dir-python_py.test +++ b/test/baseline/dir-python_py.test @@ -1,10 +1,11 @@ python import os - def check_path(path_value): - return os.path.isfile(path_value) + def check_path(path): + return os.path.isfile(path) tag PATH check check_path(value) + check os.path.isfile(value) 2012-02-29 KFC ; PATH: test/baseline/feat-import_py.test @@ -22,5 +23,6 @@ test reg 12-Feb-29 KFC Expenses:Food $20 $20 Assets:Cash $-20 0 __ERROR__ -Warning: "$sourcepath/test/baseline/dir-python_py.test", line 17: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value) +Warning: "$sourcepath/test/baseline/dir-python_py.test", line 18: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): ((os.path).isfile(value)) end test -- cgit v1.2.3 From 59f5ebe2dfe7cc93e36377f0251691e4de7b83b4 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Fri, 9 Mar 2012 03:51:53 -0600 Subject: Reworked the way that options are handled --- doc/ledger.1 | 3 +- src/chain.cc | 11 +- src/draft.cc | 2 +- src/option.h | 77 ++--- src/print.cc | 17 +- src/pyinterp.h | 4 +- src/report.cc | 240 ++++++++------- src/report.h | 635 ++++++++++++++++++---------------------- src/session.h | 18 +- test/baseline/opt-no-pager.test | 0 10 files changed, 468 insertions(+), 539 deletions(-) create mode 100644 test/baseline/opt-no-pager.test (limited to 'src/pyinterp.h') diff --git a/doc/ledger.1 b/doc/ledger.1 index 2fb074cb..a948d5a6 100644 --- a/doc/ledger.1 +++ b/doc/ledger.1 @@ -1,4 +1,4 @@ -.Dd March 7, 2012 +.Dd March 9, 2012 .Dt ledger 1 .Sh NAME .Nm ledger @@ -359,6 +359,7 @@ See .It Fl \-meta-width Ar INT .It Fl \-monthly Pq Fl M .It Fl \-no-color +.It Fl \-no-pager .It Fl \-no-rounding .It Fl \-no-titles .It Fl \-no-total diff --git a/src/chain.cc b/src/chain.cc index f8f0aeff..44b3db82 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -88,10 +88,9 @@ post_handler_ptr chain_pre_post_handlers(post_handler_ptr base_handler, predicate_t(report.HANDLER(forecast_while_).str(), report.what_to_keep()), report, - report.HANDLED(forecast_years_) ? - static_cast - (report.HANDLER(forecast_years_).value.to_long()) : - 5UL); + (report.HANDLED(forecast_years_) ? + lexical_cast + (report.HANDLER(forecast_years_).value) : 5UL)); forecast_handler->add_period_xacts(report.session.journal->period_xacts); handler.reset(forecast_handler); @@ -137,9 +136,9 @@ post_handler_ptr chain_post_handlers(post_handler_ptr base_handler, handler.reset (new truncate_xacts(handler, report.HANDLED(head_) ? - report.HANDLER(head_).value.to_int() : 0, + lexical_cast(report.HANDLER(head_).value) : 0, report.HANDLED(tail_) ? - report.HANDLER(tail_).value.to_int() : 0)); + lexical_cast(report.HANDLER(tail_).value) : 0)); // display_filter_posts adds virtual posts to the list to account // for changes in value of commodities, which otherwise would affect diff --git a/src/draft.cc b/src/draft.cc index 9abc769e..74a6f4d2 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -522,7 +522,7 @@ value_t xact_command(call_scope_t& args) xact_t * new_xact = draft.insert(*report.session.journal.get()); // Only consider actual postings for the "xact" command - report.HANDLER(limit_).on(string("#xact"), "actual"); + report.HANDLER(limit_).on("#xact", "actual"); if (new_xact) report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); diff --git a/src/option.h b/src/option.h index 38431f9d..f892b00e 100644 --- a/src/option.h +++ b/src/option.h @@ -61,9 +61,9 @@ protected: option_t& operator=(const option_t&); public: - T * parent; - value_t value; - bool wants_arg; + T * parent; + string value; + bool wants_arg; option_t(const char * _name, const char _ch = '\0') : name(_name), name_len(std::strlen(name)), ch(_ch), @@ -94,7 +94,8 @@ public: out << std::right << desc(); if (wants_arg) { out << " = "; - value.print(out, 42); + out.width(42); + out << value; } else { out.width(45); out << ' '; @@ -123,43 +124,49 @@ public: return handled; } - string& str() { + string str() const { assert(handled); - if (! value) + if (value.empty()) throw_(std::runtime_error, _("No argument provided for %1") << desc()); - return value.as_string_lval(); + return value; } - string str() const { - assert(handled); - if (! value) - throw_(std::runtime_error, _("No argument provided for %1") << desc()); - return value.as_string(); + void on(const char * whence) { + on(string(whence)); } + void on(const optional& whence) { + handler_thunk(whence); - void on_only(const optional& whence) { handled = true; source = whence; } - void on(const optional& whence, const string& str) { - on_with(whence, string_value(str)); + + void on(const char * whence, const string& str) { + on(string(whence), str); } - virtual void on_with(const optional& whence, - const value_t& val) { + void on(const optional& whence, const string& str) { + string before = value; + + handler_thunk(whence, str); + + if (value == before) + value = str; + handled = true; - value = val; source = whence; } void off() { handled = false; - value = value_t(); + value = ""; source = none; } - virtual void handler_thunk(call_scope_t&) {} + virtual void handler_thunk(const optional& whence) {} + virtual void handler_thunk(const optional& whence, + const string& str) {} - virtual void handler(call_scope_t& args) { + value_t handler(call_scope_t& args) { if (wants_arg) { if (args.size() < 2) throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -167,7 +174,7 @@ public: throw_(std::runtime_error, _("To many arguments provided for %1") << desc()); else if (! args[0].is_string()) throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); - on_with(args.get(0), args[1]); + on(args.get(0), args.get(1)); } else if (args.size() < 1) { throw_(std::runtime_error, _("No argument provided for %1") << desc()); @@ -176,27 +183,18 @@ public: throw_(std::runtime_error, _("Context argument for %1 not a string") << desc()); } else { - on_only(args.get(0)); + on(args.get(0)); } - - handler_thunk(args); - } - - virtual value_t handler_wrapper(call_scope_t& args) { - handler(args); return true; } virtual value_t operator()(call_scope_t& args) { if (! args.empty()) { args.push_front(string_value("?expr")); - return handler_wrapper(args); + return handler(args); } else if (wants_arg) { - if (handled) - return value; - else - return NULL_VALUE; + return string_value(value); } else { return handled; @@ -215,15 +213,16 @@ public: vartype var ; \ name ## option_t() : option_t(#name), var value -#define DO() virtual void handler_thunk(call_scope_t&) -#define DO_(var) virtual void handler_thunk(call_scope_t& var) +#define DO() virtual void handler_thunk(const optional& whence) +#define DO_(var) virtual void handler_thunk(const optional& whence, \ + const string& var) #define END(name) name ## handler #define COPY_OPT(name, other) name ## handler(other.name ## handler) #define MAKE_OPT_HANDLER(type, x) \ - expr_t::op_t::wrap_functor(bind(&option_t::handler_wrapper, x, _1)) + expr_t::op_t::wrap_functor(bind(&option_t::handler, x, _1)) #define MAKE_OPT_FUNCTOR(type, x) \ expr_t::op_t::wrap_functor(bind(&option_t::operator(), x, _1)) @@ -284,6 +283,10 @@ inline bool is_eq(const char * p, const char * n) { } \ END(name) +#define OTHER(name) \ + parent->HANDLER(name).parent = parent; \ + parent->HANDLER(name) + bool process_option(const string& whence, const string& name, scope_t& scope, const char * arg, const string& varname); diff --git a/src/print.cc b/src/print.cc index c544c4e0..9e52ce95 100644 --- a/src/print.cc +++ b/src/print.cc @@ -133,7 +133,7 @@ namespace { std::size_t columns = (report.HANDLED(columns_) ? - static_cast(report.HANDLER(columns_).value.to_long()) : 80); + lexical_cast(report.HANDLER(columns_).str()) : 80); if (xact.note) print_note(out, *xact.note, xact.has_flags(ITEM_NOTE_ON_NEXT_LINE), @@ -191,8 +191,8 @@ namespace { unistring name(pbuf.str()); std::size_t account_width = - (report.HANDLER(account_width_).specified ? - static_cast(report.HANDLER(account_width_).value.to_long()) : 36); + (report.HANDLED(account_width_) ? + lexical_cast(report.HANDLER(account_width_).str()) : 36); if (account_width < name.length()) account_width = name.length(); @@ -218,13 +218,14 @@ namespace { // first. } else { - int amount_width = - (report.HANDLER(amount_width_).specified ? - report.HANDLER(amount_width_).value.to_int() : 12); + std::size_t amount_width = + (report.HANDLED(amount_width_) ? + lexical_cast(report.HANDLER(amount_width_).str()) : + 12); std::ostringstream amt_str; - value_t(post->amount).print(amt_str, amount_width, -1, - AMOUNT_PRINT_RIGHT_JUSTIFY | + value_t(post->amount).print(amt_str, static_cast(amount_width), + -1, AMOUNT_PRINT_RIGHT_JUSTIFY | AMOUNT_PRINT_NO_COMPUTED_ANNOTATIONS); amt = amt_str.str(); } diff --git a/src/pyinterp.h b/src/pyinterp.h index 8699f69d..556b1563 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -136,8 +136,8 @@ public: virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, const string& name); - OPTION_(python_interpreter_t, import_, DO_(args) { - parent->import_option(args.get(1)); + OPTION_(python_interpreter_t, import_, DO_(str) { + parent->import_option(str); }); }; diff --git a/src/report.cc b/src/report.cc index 02fd7c18..110d33ee 100644 --- a/src/report.cc +++ b/src/report.cc @@ -59,7 +59,7 @@ void report_t::normalize_options(const string& verb) #ifdef HAVE_ISATTY if (! HANDLED(force_color)) { if (! HANDLED(no_color) && isatty(STDOUT_FILENO)) - HANDLER(color).on_only(string("?normalize")); + HANDLER(color).on("?normalize"); if (HANDLED(color) && ! isatty(STDOUT_FILENO)) HANDLER(color).off(); } @@ -83,7 +83,7 @@ void report_t::normalize_options(const string& verb) if (session.HANDLED(price_exp_)) commodity_pool_t::current_pool->quote_leeway = - session.HANDLER(price_exp_).value.as_long(); + lexical_cast(session.HANDLER(price_exp_).value) * 3600L; if (session.HANDLED(price_db_)) commodity_pool_t::current_pool->price_db = session.HANDLER(price_db_).str(); @@ -106,39 +106,35 @@ void report_t::normalize_options(const string& verb) if (! HANDLED(meta_width_)) { string::size_type i = HANDLER(meta_).str().find(':'); if (i != string::npos) { - HANDLED(meta_width_).on_with - (string("?normalize"), - lexical_cast(string(HANDLER(meta_).str(), i + 1))); - HANDLED(meta_).on(string("?normalize"), + HANDLED(meta_width_).on("?normalize", + string(HANDLER(meta_).str(), i + 1)); + HANDLED(meta_).on("?normalize", string(HANDLER(meta_).str(), 0, i)); } } if (HANDLED(meta_width_)) { - HANDLER(prepend_format_).on - (string("?normalize"), - string("%(justify(truncated(tag(\"") + - HANDLER(meta_).str() + "\"), " + - HANDLED(meta_width_).value.to_string() + " - 1), " + - HANDLED(meta_width_).value.to_string() + "))"); - meta_width = HANDLED(meta_width_).value.to_long(); + HANDLER(prepend_format_) + .on("?normalize", string("%(justify(truncated(tag(\"") + + HANDLER(meta_).str() + "\"), " + + HANDLED(meta_width_).value + " - 1), " + + HANDLED(meta_width_).value + "))"); + meta_width = lexical_cast(HANDLED(meta_width_).value); } else { - HANDLER(prepend_format_).on(string("?normalize"), string("%(tag(\"") + - HANDLER(meta_).str() + "\"))"); + HANDLER(prepend_format_) + .on("?normalize", string("%(tag(\"") + HANDLER(meta_).str() + "\"))"); } } - if (! HANDLED(prepend_width_)) - HANDLER(prepend_width_).on_with(string("?normalize"), static_cast(0)); if (verb == "print" || verb == "xact" || verb == "dump") { - HANDLER(related).on_only(string("?normalize")); - HANDLER(related_all).on_only(string("?normalize")); + HANDLER(related_all).parent = this; + HANDLER(related_all).on("?normalize"); } else if (verb == "equity") { - HANDLER(equity).on_only(string("?normalize")); + HANDLER(equity).on("?normalize"); } if (verb[0] != 'b' && verb[0] != 'r') - HANDLER(base).on_only(string("?normalize")); + HANDLER(base).on("?normalize"); // If a time period was specified with -p, check whether it also gave a // begin and/or end to the report period (though these can be overridden @@ -152,12 +148,10 @@ void report_t::normalize_options(const string& verb) // to avoid option ordering issues were we to have done it during the // initial parsing of the options. if (HANDLED(amount_data)) { - HANDLER(format_) - .on_with(string("?normalize"), HANDLER(plot_amount_format_).value); + HANDLER(format_).on("?normalize", HANDLER(plot_amount_format_).value); } else if (HANDLED(total_data)) { - HANDLER(format_) - .on_with(string("?normalize"), HANDLER(plot_total_format_).value); + HANDLER(format_).on("?normalize", HANDLER(plot_total_format_).value); } // If the --exchange (-X) option was used, parse out any final price @@ -170,7 +164,7 @@ void report_t::normalize_options(const string& verb) long cols = 0; if (HANDLED(columns_)) - cols = HANDLER(columns_).value.to_long(); + cols = lexical_cast(HANDLER(columns_).value); else if (const char * columns = std::getenv("COLUMNS")) cols = lexical_cast(columns); else @@ -182,23 +176,20 @@ void report_t::normalize_options(const string& verb) if (cols > 0) { DEBUG("auto.columns", "cols = " << cols); - if (! HANDLER(date_width_).specified) - HANDLER(date_width_) - .on_with(none, static_cast(format_date(CURRENT_DATE(), - FMT_PRINTED).length())); - - long date_width = HANDLER(date_width_).value.to_long(); - long payee_width = (HANDLER(payee_width_).specified ? - HANDLER(payee_width_).value.to_long() : - int(double(cols) * 0.263157)); - long account_width = (HANDLER(account_width_).specified ? - HANDLER(account_width_).value.to_long() : - int(double(cols) * 0.302631)); - long amount_width = (HANDLER(amount_width_).specified ? - HANDLER(amount_width_).value.to_long() : - int(double(cols) * 0.157894)); - long total_width = (HANDLER(total_width_).specified ? - HANDLER(total_width_).value.to_long() : + long date_width = (HANDLED(date_width_) ? + lexical_cast(HANDLER(date_width_).str()) : + format_date(CURRENT_DATE(),FMT_PRINTED).length()); + long payee_width = (HANDLED(payee_width_) ? + lexical_cast(HANDLER(payee_width_).str()) : + long(double(cols) * 0.263157)); + long account_width = (HANDLED(account_width_) ? + lexical_cast(HANDLER(account_width_).str()) : + long(double(cols) * 0.302631)); + long amount_width = (HANDLED(amount_width_) ? + lexical_cast(HANDLER(amount_width_).str()) : + long(double(cols) * 0.157894)); + long total_width = (HANDLED(total_width_) ? + lexical_cast(HANDLER(total_width_).str()) : amount_width); DEBUG("auto.columns", "date_width = " << date_width); @@ -207,11 +198,11 @@ void report_t::normalize_options(const string& verb) DEBUG("auto.columns", "amount_width = " << amount_width); DEBUG("auto.columns", "total_width = " << total_width); - if (! HANDLER(date_width_).specified && - ! HANDLER(payee_width_).specified && - ! HANDLER(account_width_).specified && - ! HANDLER(amount_width_).specified && - ! HANDLER(total_width_).specified) { + if (! HANDLED(date_width_) && + ! HANDLED(payee_width_) && + ! HANDLED(account_width_) && + ! HANDLED(amount_width_) && + ! HANDLED(total_width_)) { long total = (4 /* the spaces between */ + date_width + payee_width + account_width + amount_width + total_width); if (total > cols) { @@ -222,17 +213,19 @@ void report_t::normalize_options(const string& verb) } if (! HANDLED(meta_width_)) - HANDLER(meta_width_).on_with(string("?normalize"), 0L); - if (! HANDLER(date_width_).specified) - HANDLER(date_width_).on_with(string("?normalize"), date_width); - if (! HANDLER(payee_width_).specified) - HANDLER(payee_width_).on_with(string("?normalize"), payee_width); - if (! HANDLER(account_width_).specified) - HANDLER(account_width_).on_with(string("?normalize"), account_width); - if (! HANDLER(amount_width_).specified) - HANDLER(amount_width_).on_with(string("?normalize"), amount_width); - if (! HANDLER(total_width_).specified) - HANDLER(total_width_).on_with(string("?normalize"), total_width); + HANDLER(meta_width_).value = "0"; + if (! HANDLED(prepend_width_)) + HANDLER(prepend_width_).value = "0"; + if (! HANDLED(date_width_)) + HANDLER(date_width_).value = to_string(date_width); + if (! HANDLED(payee_width_)) + HANDLER(payee_width_).value = to_string(payee_width); + if (! HANDLED(account_width_)) + HANDLER(account_width_).value = to_string(account_width); + if (! HANDLED(amount_width_)) + HANDLER(amount_width_).value = to_string(amount_width); + if (! HANDLED(total_width_)) + HANDLER(total_width_).value = to_string(total_width); } } @@ -255,7 +248,7 @@ void report_t::normalize_period() if (! interval.duration) HANDLER(period_).off(); else if (! HANDLED(sort_all_)) - HANDLER(sort_xacts_).on_only(string("?normalize")); + HANDLER(sort_xacts_).on("?normalize"); } void report_t::parse_query_args(const value_t& args, const string& whence) @@ -278,7 +271,7 @@ void report_t::parse_query_args(const value_t& args, const string& whence) } if (query.has_query(query_t::QUERY_BOLD)) { - HANDLER(bold_if_).set_expr(whence, query.get_query(query_t::QUERY_BOLD)); + HANDLER(bold_if_).on(whence, query.get_query(query_t::QUERY_BOLD)); DEBUG("report.predicate", "Bolding predicate = " << HANDLER(bold_if_).str()); } @@ -329,9 +322,9 @@ void report_t::generate_report(post_handler_ptr handler) generate_posts_iterator walker (session, HANDLED(seed_) ? - static_cast(HANDLER(seed_).value.to_long()) : 0, + lexical_cast(HANDLER(seed_).str()) : 0, HANDLED(head_) ? - static_cast(HANDLER(head_).value.to_long()) : 50); + lexical_cast(HANDLER(head_).str()) : 50); pass_down_posts(handler, walker); } @@ -527,16 +520,17 @@ value_t report_t::fn_market(call_scope_t& args) arg0 = tmp; } + string target_commodity; if (args.has(2)) - result = arg0.exchange_commodities(args.get(2), + target_commodity = args.get(2); + + if (! target_commodity.empty()) + result = arg0.exchange_commodities(target_commodity, /* add_prices= */ false, moment); else result = arg0.value(moment); - if (! result.is_null()) - return result; - - return args[0]; + return ! result.is_null() ? result : arg0; } value_t report_t::fn_get_at(call_scope_t& args) @@ -1245,7 +1239,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "display_total")) return MAKE_FUNCTOR(report_t::fn_display_total); else if (is_eq(p, "date")) - return MAKE_FUNCTOR(report_t::fn_now); + return MAKE_FUNCTOR(report_t::fn_today); break; case 'f': @@ -1404,85 +1398,98 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return MAKE_OPT_HANDLER(report_t, handler); break; -#define POSTS_REPORT(formatter) \ +#define POSTS_REPORTER(formatter) \ WRAP_FUNCTOR(reporter<>(post_handler_ptr(formatter), *this, \ - string("#") + p)); + string("#") + p)) // Can't use WRAP_FUNCTOR here because the template arguments // confuse the parser -#define POSTS_REPORT_(method, formatter) \ - expr_t::op_t::wrap_functor \ - (reporter \ - (post_handler_ptr(formatter), *this, string("#") + p)); - -#define ACCOUNTS_REPORT(formatter) \ +#define POSTS_REPORTER_(method, formatter) \ + expr_t::op_t::wrap_functor \ + (reporter \ + (post_handler_ptr(formatter), *this, string("#") + p)) + +#define FORMATTED_POSTS_REPORTER(format) \ + POSTS_REPORTER \ + (new format_posts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) + +#define FORMATTED_COMMODITIES_REPORTER(format) \ + POSTS_REPORTER_ \ + (&report_t::commodities_report, \ + new format_posts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) + +#define ACCOUNTS_REPORTER(formatter) \ expr_t::op_t::wrap_functor(reporter \ (acct_handler_ptr(formatter), *this, \ - string("#") + p)); + string("#") + p)) + +#define FORMATTED_ACCOUNTS_REPORTER(format) \ + ACCOUNTS_REPORTER \ + (new format_accounts \ + (*this, report_format(HANDLER(format)), \ + maybe_format(HANDLER(prepend_format_)), \ + HANDLED(prepend_width_) ? \ + lexical_cast(HANDLER(prepend_width_).str()) : 0)) case symbol_t::COMMAND: switch (*p) { case 'a': if (is_eq(p, "accounts")) { - return POSTS_REPORT(new report_accounts(*this)); + return POSTS_REPORTER(new report_accounts(*this)); } break; case 'b': if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(balance_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_ACCOUNTS_REPORTER(balance_format_); } else if (is_eq(p, "budget")) { - HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)"); + HANDLER(amount_).on(string("#budget"), "(amount, 0)"); budget_flags |= BUDGET_WRAP_VALUES; if (! (budget_flags & ~BUDGET_WRAP_VALUES)) budget_flags |= BUDGET_BUDGETED; - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(budget_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_ACCOUNTS_REPORTER(budget_format_); } break; case 'c': if (is_eq(p, "csv")) { - return POSTS_REPORT(new format_posts - (*this, report_format(HANDLER(csv_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_POSTS_REPORTER(csv_format_); } else if (is_eq(p, "cleared")) { - HANDLER(amount_).set_expr(string("#cleared"), - "(amount, cleared ? amount : 0)"); - return ACCOUNTS_REPORT(new format_accounts - (*this, report_format(HANDLER(cleared_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + HANDLER(amount_).on(string("#cleared"), + "(amount, cleared ? amount : 0)"); + return FORMATTED_ACCOUNTS_REPORTER(cleared_format_); } else if (is_eq(p, "convert")) { return WRAP_FUNCTOR(convert_command); } else if (is_eq(p, "commodities")) { - return POSTS_REPORT(new report_commodities(*this)); + return POSTS_REPORTER(new report_commodities(*this)); } break; case 'e': if (is_eq(p, "equity")) { - HANDLER(generated).on_only(string("#equity")); - return POSTS_REPORT(new print_xacts(*this)); + HANDLER(generated).on("#equity"); + return POSTS_REPORTER(new print_xacts(*this)); } else if (is_eq(p, "entry")) { return WRAP_FUNCTOR(xact_command); } else if (is_eq(p, "emacs")) { - return POSTS_REPORT(new format_emacs_posts(output_stream)); + return POSTS_REPORTER(new format_emacs_posts(output_stream)); } else if (is_eq(p, "echo")) { return MAKE_FUNCTOR(report_t::echo_command); @@ -1491,43 +1498,32 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'o': if (is_eq(p, "org")) { - return POSTS_REPORT(new posts_to_org_table + return POSTS_REPORTER(new posts_to_org_table (*this, maybe_format(HANDLER(prepend_format_)))); } break; case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) { - return POSTS_REPORT(new print_xacts(*this, HANDLED(raw))); + return POSTS_REPORTER(new print_xacts(*this, HANDLED(raw))); } else if (is_eq(p, "prices")) { - return POSTS_REPORT_(&report_t::commodities_report, - new format_posts - (*this, report_format(HANDLER(prices_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_COMMODITIES_REPORTER(prices_format_); } else if (is_eq(p, "pricedb")) { - return POSTS_REPORT_(&report_t::commodities_report, - new format_posts - (*this, report_format(HANDLER(pricedb_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_COMMODITIES_REPORTER(pricedb_format_); } else if (is_eq(p, "pricemap")) { return MAKE_FUNCTOR(report_t::pricemap_command); } else if (is_eq(p, "payees")) { - return POSTS_REPORT(new report_payees(*this)); + return POSTS_REPORTER(new report_payees(*this)); } break; case 'r': if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) { - return POSTS_REPORT(new format_posts - (*this, report_format(HANDLER(register_format_)), - maybe_format(HANDLER(prepend_format_)), - HANDLER(prepend_width_).value.to_size_t())); + return FORMATTED_POSTS_REPORTER(register_format_); } else if (is_eq(p, "reload")) { return MAKE_FUNCTOR(report_t::reload_command); @@ -1545,7 +1541,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, if (is_eq(p, "xact")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "xml")) - return POSTS_REPORT(new format_xml(*this)); + return POSTS_REPORTER(new format_xml(*this)); break; } break; @@ -1568,8 +1564,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, break; case 'g': if (is_eq(p, "generate")) - return POSTS_REPORT_(&report_t::generate_report, - new print_xacts(*this)); + return POSTS_REPORTER_(&report_t::generate_report, + new print_xacts(*this)); break; case 'p': if (is_eq(p, "parse")) diff --git a/src/report.h b/src/report.h index d68d1f75..2ad53f01 100644 --- a/src/report.h +++ b/src/report.h @@ -353,12 +353,16 @@ public: * Option handlers */ - OPTION__(report_t, abbrev_len_, - CTOR(report_t, abbrev_len_) { on_with(none, 2L); }); + OPTION__ + (report_t, abbrev_len_, + CTOR(report_t, abbrev_len_) { + on(none, "2"); + }); + OPTION(report_t, account_); OPTION_(report_t, actual, DO() { // -L - parent->HANDLER(limit_).on(string("--actual"), "actual"); + OTHER(limit_).on(whence, "actual"); }); OPTION_(report_t, add_budget, DO() { @@ -368,12 +372,8 @@ public: OPTION__ (report_t, amount_, // -t DECL1(report_t, amount_, merged_expr_t, expr, ("amount_expr", "amount")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, amount_data); // -j @@ -381,216 +381,204 @@ public: OPTION(report_t, auto_match); OPTION_(report_t, average, DO() { // -A - parent->HANDLER(display_total_) - .set_expr(string("--average"), "count>0?(display_total/count):0"); + OTHER(display_total_) + .on(whence, "count>0?(display_total/count):0"); }); - OPTION__(report_t, balance_format_, CTOR(report_t, balance_format_) { - on(none, - "%(ansify_if(" - " justify(scrub(display_total), 20, 20 + prepend_width, true, color)," - " bold if should_bold))" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(" - " ansify_if(partial_account(options.flat), blue if color)," - " bold if should_bold))\n%/" - "%$1\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "--------------------\n"); - }); + OPTION__ + (report_t, balance_format_, + CTOR(report_t, balance_format_) { + on(none, + "%(ansify_if(" + " justify(scrub(display_total), 20," + " 20 + int(prepend_width), true, color)," + " bold if should_bold))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(" + " ansify_if(partial_account(options.flat), blue if color)," + " bold if should_bold))\n%/" + "%$1\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "--------------------\n"); + }); OPTION(report_t, base); OPTION_(report_t, basis, DO() { // -B - parent->HANDLER(revalued).on_only(string("--basis")); - parent->HANDLER(amount_).expr.set_base_expr("rounded(cost)"); + OTHER(revalued).on(whence); + OTHER(amount_).expr.set_base_expr("rounded(cost)"); }); - OPTION_(report_t, begin_, DO_(args) { // -b - date_interval_t interval(args.get(1)); - optional begin = interval.begin(); - if (! begin) + OPTION_(report_t, begin_, DO_(str) { // -b + date_interval_t interval(str); + if (optional begin = interval.begin()) { + string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; + OTHER(limit_).on(whence, predicate); + } else { throw_(std::invalid_argument, - _("Could not determine beginning of period '%1'") - << args.get(1)); - - string predicate = "date>=[" + to_iso_extended_string(*begin) + "]"; - parent->HANDLER(limit_).on(string("--begin"), predicate); + _("Could not determine beginning of period '%1'") << str); + } }); - OPTION__ + OPTION_ (report_t, bold_if_, expr_t expr; - CTOR(report_t, bold_if_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION_(report_t, budget, DO() { parent->budget_flags |= BUDGET_BUDGETED; }); - OPTION__(report_t, budget_format_, CTOR(report_t, budget_format_) { - on(none, - "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" - " %(justify(-scrub(get_at(display_total, 1)), 12, " - " 12 + 1 + 12, true, color))" - " %(justify(scrub(get_at(display_total, 1) + " - " get_at(display_total, 0)), 12, " - " 12 + 1 + 12 + 1 + 12, true, color))" - " %(ansify_if(" - " justify((get_at(display_total, 1) ? " - " (100% * scrub(get_at(display_total, 0))) / " - " -scrub(get_at(display_total, 1)) : 0), " - " 5, -1, true, false)," - " magenta if (color and get_at(display_total, 1) and " - " (abs(quantity(scrub(get_at(display_total, 0))) / " - " quantity(scrub(get_at(display_total, 1)))) >= 1))))" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(partial_account(options.flat), blue if color))\n" - "%/%$1 %$2 %$3 %$4\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "------------ ------------ ------------ -----\n"); - }); + OPTION__ + (report_t, budget_format_, + CTOR(report_t, budget_format_) { + on(none, + "%(justify(scrub(get_at(display_total, 0)), 12, -1, true, color))" + " %(justify(-scrub(get_at(display_total, 1)), 12, " + " 12 + 1 + 12, true, color))" + " %(justify(scrub(get_at(display_total, 1) + " + " get_at(display_total, 0)), 12, " + " 12 + 1 + 12 + 1 + 12, true, color))" + " %(ansify_if(" + " justify((get_at(display_total, 1) ? " + " (100% * scrub(get_at(display_total, 0))) / " + " -scrub(get_at(display_total, 1)) : 0), " + " 5, -1, true, false)," + " magenta if (color and get_at(display_total, 1) and " + " (abs(quantity(scrub(get_at(display_total, 0))) / " + " quantity(scrub(get_at(display_total, 1)))) >= 1))))" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(partial_account(options.flat), blue if color))\n" + "%/%$1 %$2 %$3 %$4\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "------------ ------------ ------------ -----\n"); + }); OPTION(report_t, by_payee); // -P OPTION_(report_t, cleared, DO() { // -C - parent->HANDLER(limit_).on(string("--cleared"), "cleared"); + OTHER(limit_).on(whence, "cleared"); }); - OPTION__(report_t, cleared_format_, CTOR(report_t, cleared_format_) { - on(none, - "%(justify(scrub(get_at(display_total, 0)), 16, 16 + prepend_width, " - " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " - " 36 + prepend_width, true, color))" - " %(latest_cleared ? format_date(latest_cleared) : \" \")" - " %(!options.flat ? depth_spacer : \"\")" - "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" - "%$1 %$2 %$3\n%/" - "%(prepend_width ? \" \" * prepend_width : \"\")" - "---------------- ---------------- ---------\n"); - }); + OPTION__ + (report_t, cleared_format_, + CTOR(report_t, cleared_format_) { + on(none, + "%(justify(scrub(get_at(display_total, 0)), 16, 16 + int(prepend_width), " + " true, color)) %(justify(scrub(get_at(display_total, 1)), 18, " + " 36 + int(prepend_width), true, color))" + " %(latest_cleared ? format_date(latest_cleared) : \" \")" + " %(!options.flat ? depth_spacer : \"\")" + "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" + "%$1 %$2 %$3\n%/" + "%(prepend_width ? \" \" * int(prepend_width) : \"\")" + "---------------- ---------------- ---------\n"); + }); OPTION(report_t, color); OPTION_(report_t, collapse, DO() { // -n // Make sure that balance reports are collapsed too, but only apply it // to account xacts - parent->HANDLER(display_).on(string("--collapse"), "post|depth<=1"); + OTHER(display_).on(whence, "post|depth<=1"); }); OPTION_(report_t, collapse_if_zero, DO() { - parent->HANDLER(collapse).on_only(string("--collapse-if-zero")); + OTHER(collapse).on(whence); }); OPTION(report_t, columns_); OPTION(report_t, count); - OPTION__(report_t, csv_format_, CTOR(report_t, csv_format_) { - on(none, - "%(quoted(date))," - "%(quoted(code))," - "%(quoted(payee))," - "%(quoted(display_account))," - "%(quoted(commodity))," - "%(quoted(quantity(scrub(display_amount))))," - "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," - "%(quoted(join(note | xact.note)))\n"); - }); + OPTION__ + (report_t, csv_format_, + CTOR(report_t, csv_format_) { + on(none, + "%(quoted(date))," + "%(quoted(code))," + "%(quoted(payee))," + "%(quoted(display_account))," + "%(quoted(commodity))," + "%(quoted(quantity(scrub(display_amount))))," + "%(quoted(cleared ? \"*\" : (pending ? \"!\" : \"\")))," + "%(quoted(join(note | xact.note)))\n"); + }); OPTION_(report_t, current, DO() { // -c - parent->HANDLER(limit_).on(string("--current"), "date<=today"); + OTHER(limit_).on(whence, "date<=today"); }); OPTION_(report_t, daily, DO() { // -D - parent->HANDLER(period_).on(string("--daily"), "daily"); + OTHER(period_).on(whence, "daily"); }); OPTION(report_t, date_); OPTION(report_t, date_format_); OPTION(report_t, datetime_format_); - OPTION_(report_t, depth_, DO_(args) { - parent->HANDLER(display_) - .on(string("--depth"), string("depth<=") + args.get(1)); + OPTION_(report_t, depth_, DO_(str) { + OTHER(display_).on(whence, string("depth<=") + str); }); OPTION_(report_t, deviation, DO() { - parent->HANDLER(display_total_) - .set_expr(string("--deviation"), "display_amount-display_total"); + OTHER(display_total_) + .on(whence, "display_amount-display_total"); }); - OPTION__ - (report_t, display_, // -d - CTOR(report_t, display_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + OPTION_ + (report_t, display_, + DO_(str) { // -d + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION__ (report_t, display_amount_, DECL1(report_t, display_amount_, merged_expr_t, expr, ("display_amount", "amount_expr")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION__ (report_t, display_total_, DECL1(report_t, display_total_, merged_expr_t, expr, ("display_total", "total_expr")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, dow); OPTION(report_t, aux_date); OPTION(report_t, empty); // -E - OPTION_(report_t, end_, DO_(args) { // -e - date_interval_t interval(args.get(1)); + OPTION_(report_t, end_, DO_(str) { // -e // Use begin() here so that if the user says --end=2008, we end on // 2008/01/01 instead of 2009/01/01 (which is what end() would // return). - optional end = interval.begin(); - if (! end) + date_interval_t interval(str); + if (optional end = interval.begin()) { + string predicate = "date<[" + to_iso_extended_string(*end) + "]"; + OTHER(limit_).on(whence, predicate); + + parent->terminus = datetime_t(*end); + } else { throw_(std::invalid_argument, _("Could not determine end of period '%1'") - << args.get(1)); - - string predicate = "date<[" + to_iso_extended_string(*end) + "]"; - parent->HANDLER(limit_).on(string("--end"), predicate); - - parent->terminus = datetime_t(*end); + << str); + } }); OPTION(report_t, equity); OPTION(report_t, exact); - OPTION_(report_t, exchange_, DO_(args) { // -X - on_with(args.get(0), args[1]); - call_scope_t no_args(*parent); - no_args.push_back(args[0]); - parent->HANDLER(market).parent = parent; - parent->HANDLER(market).handler(no_args); + OPTION_(report_t, exchange_, DO_() { // -X + // Using -X implies -V. The main difference is that now + // HANDLER(exchange_) contains the name of a commodity, which + // is accessed via the "exchange" value expression function. + OTHER(market).on(whence); }); OPTION(report_t, flat); @@ -601,74 +589,65 @@ public: OPTION(report_t, format_); // -F OPTION_(report_t, gain, DO() { // -G - parent->HANDLER(revalued).on_only(string("--gain")); + OTHER(revalued).on(whence); - parent->HANDLER(amount_).expr.set_base_expr("(amount, cost)"); - parent->HANDLER(total_).expr.set_base_expr("total"); + OTHER(amount_).expr.set_base_expr("(amount, cost)"); + OTHER(total_).expr.set_base_expr("total"); // Since we are displaying the amounts of revalued postings, they // will end up being composite totals, and hence a pair of pairs. - parent->HANDLER(display_amount_) - .set_expr(string("--gain"), - "use_direct_amount ? amount :" - " (is_seq(get_at(amount_expr, 0)) ?" - " get_at(get_at(amount_expr, 0), 0) :" - " market(get_at(amount_expr, 0), value_date, exchange)" - " - get_at(amount_expr, 1))"); - parent->HANDLER(revalued_total_) - .set_expr(string("--gain"), - "(market(get_at(total_expr, 0), value_date, exchange), " - "get_at(total_expr, 1))"); - parent->HANDLER(display_total_) - .set_expr(string("--gain"), - "use_direct_amount ? total_expr :" - " market(get_at(total_expr, 0), value_date, exchange)" - " - get_at(total_expr, 1)"); + OTHER(display_amount_) + .on(whence, + "use_direct_amount ? amount :" + " (is_seq(get_at(amount_expr, 0)) ?" + " get_at(get_at(amount_expr, 0), 0) :" + " market(get_at(amount_expr, 0), value_date, exchange)" + " - get_at(amount_expr, 1))"); + OTHER(revalued_total_) + .on(whence, + "(market(get_at(total_expr, 0), value_date, exchange), " + "get_at(total_expr, 1))"); + OTHER(display_total_) + .on(whence, + "use_direct_amount ? total_expr :" + " market(get_at(total_expr, 0), value_date, exchange)" + " - get_at(total_expr, 1)"); }); OPTION(report_t, generated); - OPTION__ + OPTION_ (report_t, group_by_, expr_t expr; - CTOR(report_t, group_by_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); - OPTION__(report_t, group_title_format_, CTOR(report_t, group_title_format_) { - on(none, "%(value)\n"); - }); + OPTION__ + (report_t, group_title_format_, + CTOR(report_t, group_title_format_) { + on(none, "%(value)\n"); + }); OPTION(report_t, head_); OPTION_(report_t, historical, DO() { // -H - parent->HANDLER(amount_) - .set_expr(string("--historical"), - "nail_down(amount_expr, (s,d,t -> market(s,value_date,t)))"); + OTHER(amount_) + .on(whence, "nail_down(amount_expr, " + "market(amount_expr, value_date, exchange))"); }); - OPTION(report_t, inject_); OPTION_(report_t, invert, DO() { - parent->HANDLER(amount_).set_expr(string("--invert"), "-amount"); + OTHER(amount_).on(whence, "-amount"); }); - OPTION__ - (report_t, limit_, // -l - CTOR(report_t, limit_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + OPTION_ + (report_t, limit_, + DO_(str) { // -l + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, lot_dates); @@ -678,49 +657,44 @@ public: OPTION(report_t, lots_actual); OPTION_(report_t, market, DO() { // -V - parent->HANDLER(revalued).on_only(string("--market")); - parent->HANDLER(display_amount_) - .set_expr(string("--market"), - "market(display_amount, value_date, exchange)"); - parent->HANDLER(display_total_) - .set_expr(string("--market"), - "market(display_total, value_date, exchange)"); + OTHER(revalued).on(whence); + + OTHER(display_amount_) + .on(whence, "market(display_amount, value_date, exchange)"); + OTHER(display_total_) + .on(whence, "market(display_total, value_date, exchange)"); }); OPTION(report_t, meta_); OPTION_(report_t, monthly, DO() { // -M - parent->HANDLER(period_).on(string("--monthly"), "monthly"); + OTHER(period_).on(whence, "monthly"); }); OPTION_(report_t, no_color, DO() { - parent->HANDLER(color).off(); + OTHER(color).off(); }); OPTION(report_t, no_rounding); OPTION(report_t, no_titles); OPTION(report_t, no_total); - OPTION_(report_t, now_, DO_(args) { - date_interval_t interval(args.get(1)); - optional begin = interval.begin(); - if (! begin) + OPTION_(report_t, now_, DO_(str) { + date_interval_t interval(str); + if (optional begin = interval.begin()) { + ledger::epoch = parent->terminus = datetime_t(*begin); + } else { throw_(std::invalid_argument, _("Could not determine beginning of period '%1'") - << args.get(1)); - ledger::epoch = parent->terminus = datetime_t(*begin); + << str); + } }); - OPTION__ + OPTION_ (report_t, only_, - CTOR(report_t, only_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(string("(") + str() + ")&(" + - text.as_string() + ")")); + DO_(str) { + if (handled) + value = string("(") + value + ")&(" + str + ")"; }); OPTION(report_t, output_); // -o @@ -741,178 +715,162 @@ public: setenv("LESS", "-FRSX", 0); // don't overwrite } } - } - virtual void on_with(const optional& whence, const value_t& text) { - string cmd(text.to_string()); - if (cmd == "" || cmd == "false" || cmd == "off" || - cmd == "none" || cmd == "no" || cmd == "disable") - option_t::off(); - else - option_t::on_with(whence, text); }); #else // HAVE_ISATTY - OPTION__ - (report_t, pager_, - CTOR(report_t, pager_) { - } - virtual void on_with(const optional& whence, const value_t& text) { - string cmd(text.to_string()); - if (cmd == "" || cmd == "false" || cmd == "off" || - cmd == "none" || cmd == "no" || cmd == "disable") - option_t::off(); - else - option_t::on_with(whence, text); - }); + OPTION(report_t, pager_); #endif // HAVE_ISATTY + OPTION_(report_t, no_pager, DO() { + OTHER(pager_).off(); + }); + OPTION(report_t, payee_); OPTION_(report_t, pending, DO() { // -C - parent->HANDLER(limit_).on(string("--pending"), "pending"); + OTHER(limit_).on(whence, "pending"); }); OPTION_(report_t, percent, DO() { // -% - parent->HANDLER(total_) - .set_expr(string("--percent"), - "((is_account&parent&parent.total)?" - " percent(scrub(total), scrub(parent.total)):0)"); + OTHER(total_) + .on(whence, + "((is_account&parent&parent.total)?" + " percent(scrub(total), scrub(parent.total)):0)"); }); - OPTION__ - (report_t, period_, // -p - CTOR(report_t, period_) {} - virtual void on_with(const optional& whence, const value_t& text) { - if (! handled) - option_t::on_with(whence, text); - else - option_t::on_with(whence, - string_value(text.as_string() + " " + str())); + OPTION_ + (report_t, period_, + DO_(str) { // -p + if (handled) + value += string(" ") + str; }); OPTION(report_t, pivot_); - OPTION__(report_t, plot_amount_format_, CTOR(report_t, plot_amount_format_) { - on(none, - "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); - }); + OPTION__ + (report_t, plot_amount_format_, + CTOR(report_t, plot_amount_format_) { + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_amount)))\n"); + }); - OPTION__(report_t, plot_total_format_, CTOR(report_t, plot_total_format_) { - on(none, - "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); - }); + OPTION__ + (report_t, plot_total_format_, + CTOR(report_t, plot_total_format_) { + on(none, + "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); + }); OPTION(report_t, prepend_format_); - OPTION_(report_t, prepend_width_, DO_(args) { - value = args.get(1); - }); + OPTION(report_t, prepend_width_); OPTION_(report_t, price, DO() { // -I - parent->HANDLER(amount_).expr.set_base_expr("price"); + OTHER(amount_).expr.set_base_expr("price"); }); - OPTION__(report_t, prices_format_, CTOR(report_t, prices_format_) { - on(none, - "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " - " 2 + 9 + 8 + 12, true, color))\n"); - }); + OPTION__ + (report_t, prices_format_, + CTOR(report_t, prices_format_) { + on(none, + "%(date) %-8(display_account) %(justify(scrub(display_amount), 12, " + " 2 + 9 + 8 + 12, true, color))\n"); + }); - OPTION__(report_t, pricedb_format_, CTOR(report_t, pricedb_format_) { - on(none, - "P %(datetime) %(display_account) %(scrub(display_amount))\n"); - }); + OPTION__ + (report_t, pricedb_format_, + CTOR(report_t, pricedb_format_) { + on(none, + "P %(datetime) %(display_account) %(scrub(display_amount))\n"); + }); OPTION(report_t, primary_date); OPTION_(report_t, quantity, DO() { // -O - parent->HANDLER(revalued).off(); - parent->HANDLER(amount_).expr.set_base_expr("amount"); - parent->HANDLER(total_).expr.set_base_expr("total"); + OTHER(revalued).off(); + + OTHER(amount_).expr.set_base_expr("amount"); + OTHER(total_).expr.set_base_expr("total"); }); OPTION_(report_t, quarterly, DO() { - parent->HANDLER(period_).on(string("--quarterly"), "quarterly"); + OTHER(period_).on(whence, "quarterly"); }); OPTION(report_t, raw); OPTION_(report_t, real, DO() { // -R - parent->HANDLER(limit_).on(string("--real"), "real"); + OTHER(limit_).on(whence, "real"); }); - OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { - on(none, - "%(ansify_if(" - " ansify_if(justify(format_date(date), date_width)," - " green if color and date > today)," - " bold if should_bold))" - " %(ansify_if(" - " ansify_if(justify(truncated(payee, payee_width), payee_width), " - " bold if color and !cleared and actual)," - " bold if should_bold))" - " %(ansify_if(" - " ansify_if(justify(truncated(display_account, account_width, " - " abbrev_len), account_width)," - " blue if color)," - " bold if should_bold))" - " %(ansify_if(" - " justify(scrub(display_amount), amount_width, " - " 3 + meta_width + date_width + payee_width" - " + account_width + amount_width + prepend_width," - " true, color)," - " bold if should_bold))" - " %(ansify_if(" - " justify(scrub(display_total), total_width, " - " 4 + meta_width + date_width + payee_width" - " + account_width + amount_width + total_width" - " + prepend_width, true, color)," - " bold if should_bold))\n%/" - "%(justify(\" \", date_width))" - " %(ansify_if(" - " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " - " payee_width), payee_width)," - " bold if should_bold))" - " %$3 %$4 %$5\n"); - }); + OPTION__ + (report_t, register_format_, + CTOR(report_t, register_format_) { + on(none, + "%(ansify_if(" + " ansify_if(justify(format_date(date), int(date_width))," + " green if color and date > today)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(payee, int(payee_width)), int(payee_width)), " + " bold if color and !cleared and actual)," + " bold if should_bold))" + " %(ansify_if(" + " ansify_if(justify(truncated(display_account, int(account_width), " + " abbrev_len), int(account_width))," + " blue if color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_amount), int(amount_width), " + " 3 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(prepend_width)," + " true, color)," + " bold if should_bold))" + " %(ansify_if(" + " justify(scrub(display_total), int(total_width), " + " 4 + int(meta_width) + int(date_width) + int(payee_width)" + " + int(account_width) + int(amount_width) + int(total_width)" + " + int(prepend_width), true, color)," + " bold if should_bold))\n%/" + "%(justify(\" \", int(date_width)))" + " %(ansify_if(" + " justify(truncated(has_tag(\"Payee\") ? payee : \" \", " + " int(payee_width)), int(payee_width))," + " bold if should_bold))" + " %$3 %$4 %$5\n"); + }); OPTION(report_t, related); // -r OPTION_(report_t, related_all, DO() { - parent->HANDLER(related).on_only(string("--related-all")); + OTHER(related).on(whence); }); OPTION(report_t, revalued); OPTION(report_t, revalued_only); - OPTION__ + OPTION_ (report_t, revalued_total_, expr_t expr; - CTOR(report_t, revalued_total_) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr = str; - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, rich_data); OPTION(report_t, seed_); - OPTION_(report_t, sort_, DO_(args) { // -S - on_with(args.get(0), args[1]); - parent->HANDLER(sort_xacts_).off(); - parent->HANDLER(sort_all_).off(); + OPTION_(report_t, sort_, DO_(str) { // -S + OTHER(sort_xacts_).off(); + OTHER(sort_all_).off(); }); - OPTION_(report_t, sort_all_, DO_(args) { - parent->HANDLER(sort_).on_with(string("--sort-all"), args[1]); - parent->HANDLER(sort_xacts_).off(); + OPTION_(report_t, sort_all_, DO_(str) { + OTHER(sort_).on(whence, str); + OTHER(sort_xacts_).off(); }); - OPTION_(report_t, sort_xacts_, DO_(args) { - parent->HANDLER(sort_).on_with(string("--sort-xacts"), args[1]); - parent->HANDLER(sort_all_).off(); + OPTION_(report_t, sort_xacts_, DO_(str) { + OTHER(sort_).on(whence, str); + OTHER(sort_all_).off(); }); OPTION(report_t, start_of_week_); @@ -922,18 +880,13 @@ public: OPTION__ (report_t, total_, // -T DECL1(report_t, total_, merged_expr_t, expr, ("total_expr", "total")) {} - void set_expr(const optional& whence, const string& str) { + DO_(str) { expr.append(str); - on(whence, str); - } - DO_(args) { - set_expr(args.get(0), args.get(1)); }); OPTION(report_t, total_data); // -J - OPTION_(report_t, truncate_, DO_(args) { - string style(args.get(1)); + OPTION_(report_t, truncate_, DO_(style) { if (style == "leading") format_t::default_style = format_t::TRUNCATE_LEADING; else if (style == "middle") @@ -951,7 +904,7 @@ public: }); OPTION_(report_t, uncleared, DO() { // -U - parent->HANDLER(limit_).on(string("--uncleared"), "uncleared|pending"); + OTHER(limit_).on(whence, "uncleared|pending"); }); OPTION(report_t, unrealized); @@ -960,48 +913,28 @@ public: OPTION(report_t, unrealized_losses_); OPTION_(report_t, unround, DO() { - parent->HANDLER(amount_) - .set_expr(string("--unround"), "unrounded(amount_expr)"); - parent->HANDLER(total_) - .set_expr(string("--unround"), "unrounded(total_expr)"); + OTHER(amount_).on(whence, "unrounded(amount_expr)"); + OTHER(total_).on(whence, "unrounded(total_expr)"); }); OPTION_(report_t, weekly, DO() { // -W - parent->HANDLER(period_).on(string("--weekly"), "weekly"); + OTHER(period_).on(whence, "weekly"); }); OPTION_(report_t, wide, DO() { // -w - parent->HANDLER(columns_).on_with(string("--wide"), 132L); + OTHER(columns_).on(whence, "132"); }); OPTION_(report_t, yearly, DO() { // -Y - parent->HANDLER(period_).on(string("--yearly"), "yearly"); + OTHER(period_).on(whence, "yearly"); }); - OPTION__(report_t, meta_width_, - bool specified; - CTOR(report_t, meta_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, date_width_, - bool specified; - CTOR(report_t, date_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, payee_width_, - bool specified; - CTOR(report_t, payee_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, account_width_, - bool specified; - CTOR(report_t, account_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, amount_width_, - bool specified; - CTOR(report_t, amount_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); - OPTION__(report_t, total_width_, - bool specified; - CTOR(report_t, total_width_) { specified = false; } - DO_(args) { value = args.get(1); specified = true; }); + OPTION(report_t, meta_width_); + OPTION(report_t, date_width_); + OPTION(report_t, payee_width_); + OPTION(report_t, account_width_); + OPTION(report_t, amount_width_); + OPTION(report_t, total_width_); }; diff --git a/src/session.h b/src/session.h index b06c4a42..cb981346 100644 --- a/src/session.h +++ b/src/session.h @@ -128,28 +128,24 @@ public: OPTION__ (session_t, price_exp_, // -Z - CTOR(session_t, price_exp_) { value = 24L * 3600L; } - DO_(args) { - value = args.get(1) * 60L; - }); + CTOR(session_t, price_exp_) { value = "24"; }); OPTION__ (session_t, file_, // -f std::list data_files; CTOR(session_t, file_) {} - DO_(args) { - assert(args.size() == 2); + DO_(str) { if (parent->flush_on_next_data_file) { data_files.clear(); parent->flush_on_next_data_file = false; } - data_files.push_back(args.get(1)); + data_files.push_back(str); }); - OPTION_(session_t, input_date_format_, DO_(args) { - // This changes static variables inside times.h, which affects the basic - // date parser. - set_input_date_format(args.get(1).c_str()); + OPTION_(session_t, input_date_format_, DO_(str) { + // This changes static variables inside times.h, which affects the + // basic date parser. + set_input_date_format(str.c_str()); }); OPTION(session_t, explicit); diff --git a/test/baseline/opt-no-pager.test b/test/baseline/opt-no-pager.test new file mode 100644 index 00000000..e69de29b -- cgit v1.2.3