Segregated symbols into 5 separate namespaces

The different namespaces are:

  Function      Value expression functions, which receive a "context"
  Option        Command-line options
  Precommand    Commands which are invoked before reading the journal
  Command       Commands which are invoked after reading the journal
  Directive     Directives that occur at column 0 in a data file

This greatly eases the ability for Python uses to add intercept hooks to
change how the basic Ledger module functions.  An example of what should
be possible soon:

  import ledger

  def my_foo_handler(value):
      print "--foo received:", value

  ledger.add_handler(ledger.Option, "foo=", my_foo_handler)
This commit is contained in:
John Wiegley 2009-11-04 20:40:07 -05:00
parent 4a14f3224b
commit 78e6770c4c
23 changed files with 509 additions and 423 deletions

View file

@ -241,8 +241,12 @@ namespace {
}
}
expr_t::ptr_op_t account_t::lookup(const string& name)
expr_t::ptr_op_t account_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
if (kind != symbol_t::FUNCTION)
return NULL;
switch (name[0]) {
case 'a':
if (name == "amount")

View file

@ -119,7 +119,8 @@ public:
}
bool remove_post(post_t * post);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
bool valid() const;

View file

@ -314,28 +314,32 @@ option_t<global_scope_t> * global_scope_t::lookup_option(const char * p)
return NULL;
}
expr_t::ptr_op_t global_scope_t::lookup(const string& name)
expr_t::ptr_op_t global_scope_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<global_scope_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(global_scope_t, handler);
}
switch (kind) {
case symbol_t::FUNCTION:
if (option_t<global_scope_t> * handler = lookup_option(name.c_str()))
return MAKE_OPT_FUNCTOR(global_scope_t, handler);
break;
case 'p':
if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
switch (*q) {
case 'p':
if (is_eq(q, "push"))
return MAKE_FUNCTOR(global_scope_t::push_command);
else if (is_eq(q, "pop"))
return MAKE_FUNCTOR(global_scope_t::pop_command);
break;
}
case symbol_t::OPTION:
if (option_t<global_scope_t> * handler = lookup_option(name.c_str()))
return MAKE_OPT_HANDLER(global_scope_t, handler);
break;
case symbol_t::PRECOMMAND: {
const char * p = name.c_str();
switch (*p) {
case 'p':
if (is_eq(p, "push"))
return MAKE_FUNCTOR(global_scope_t::push_command);
else if (is_eq(p, "pop"))
return MAKE_FUNCTOR(global_scope_t::pop_command);
break;
}
}
default:
break;
}
@ -396,7 +400,7 @@ void global_scope_t::normalize_session_options()
function_t global_scope_t::look_for_precommand(scope_t& scope,
const string& verb)
{
if (expr_t::ptr_op_t def = scope.lookup(string(PRECMD_PREFIX) + verb))
if (expr_t::ptr_op_t def = scope.lookup(symbol_t::PRECOMMAND, verb))
return def->as_function();
else
return function_t();
@ -405,7 +409,7 @@ function_t global_scope_t::look_for_precommand(scope_t& scope,
function_t global_scope_t::look_for_command(scope_t& scope,
const string& verb)
{
if (expr_t::ptr_op_t def = scope.lookup(string(CMD_PREFIX) + verb))
if (expr_t::ptr_op_t def = scope.lookup(symbol_t::COMMAND, verb))
return def->as_function();
else
return function_t();

View file

@ -117,7 +117,8 @@ See LICENSE file included with the distribution for details and disclaimer.");
option_t<global_scope_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
OPTION(global_scope_t, args_only);
OPTION(global_scope_t, debug_);

View file

@ -304,8 +304,12 @@ value_t get_comment(item_t& item)
}
}
expr_t::ptr_op_t item_t::lookup(const string& name)
expr_t::ptr_op_t item_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
if (kind != symbol_t::FUNCTION)
return NULL;
switch (name[0]) {
case 'a':
if (name == "actual")

View file

@ -189,7 +189,8 @@ public:
return _state;
}
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
bool valid() const;

View file

@ -43,7 +43,7 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
if (is_ident()) {
DEBUG("expr.compile", "lookup: " << as_ident());
if (ptr_op_t def = scope.lookup(as_ident())) {
if (ptr_op_t def = scope.lookup(symbol_t::FUNCTION, as_ident())) {
// Identifier references are first looked up at the point of
// definition, and then at the point of every use if they could
// not be found there.
@ -65,11 +65,11 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope, const int depth)
if (kind == O_DEFINE) {
switch (left()->kind) {
case IDENT:
scope.define(left()->as_ident(), right());
scope.define(symbol_t::FUNCTION, left()->as_ident(), right());
break;
case O_CALL:
if (left()->left()->is_ident())
scope.define(left()->left()->as_ident(), this);
scope.define(symbol_t::FUNCTION, left()->left()->as_ident(), this);
else
throw_(compile_error, _("Invalid function definition"));
break;
@ -152,9 +152,10 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
if (! varname->is_ident())
throw_(calc_error, _("Invalid function definition"));
else if (args_index == args_count)
local_scope.define(varname->as_ident(), wrap_value(false));
local_scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(false));
else
local_scope.define(varname->as_ident(),
local_scope.define(symbol_t::FUNCTION, varname->as_ident(),
wrap_value(call_args[args_index++]));
}
@ -178,7 +179,8 @@ value_t expr_t::op_t::calc(scope_t& scope, ptr_op_t * locus, const int depth)
_("Left operand of . operator is NULL"));
} else {
scope_t& objscope(*obj.as_scope());
if (ptr_op_t member = objscope.lookup(right()->as_ident())) {
if (ptr_op_t member =
objscope.lookup(symbol_t::FUNCTION, right()->as_ident())) {
result = member->calc(objscope, NULL, depth + 1);
break;
}

View file

@ -41,8 +41,7 @@ namespace {
op_bool_tuple find_option(scope_t& scope, const string& name)
{
char buf[128];
std::strcpy(buf, "opt_");
char * p = &buf[4];
char * p = buf;
foreach (char ch, name) {
if (ch == '-')
*p++ = '_';
@ -52,28 +51,27 @@ namespace {
*p++ = '_';
*p = '\0';
if (expr_t::ptr_op_t op = scope.lookup(buf))
if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf))
return op_bool_tuple(op, true);
*--p = '\0';
return op_bool_tuple(scope.lookup(buf), false);
return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false);
}
op_bool_tuple find_option(scope_t& scope, const char letter)
{
char buf[10];
std::strcpy(buf, "opt_");
buf[4] = letter;
buf[5] = '_';
buf[6] = '\0';
char buf[4];
buf[0] = letter;
buf[1] = '_';
buf[2] = '\0';
if (expr_t::ptr_op_t op = scope.lookup(buf))
if (expr_t::ptr_op_t op = scope.lookup(symbol_t::OPTION, buf))
return op_bool_tuple(op, true);
buf[5] = '\0';
buf[1] = '\0';
return op_bool_tuple(scope.lookup(buf), false);
return op_bool_tuple(scope.lookup(symbol_t::OPTION, buf), false);
}
void process_option(const string& whence, const function_t& opt,

View file

@ -283,30 +283,6 @@ inline bool is_eq(const char * p, const char * n) {
} \
END(name)
#define OPT_PREFIX "opt_"
#define OPT_PREFIX_LEN 4
#define WANT_OPT() \
(std::strncmp(p, OPT_PREFIX, OPT_PREFIX_LEN) == 0)
#define PRECMD_PREFIX "precmd_"
#define PRECMD_PREFIX_LEN 7
#define WANT_PRECMD() \
(std::strncmp(p, PRECMD_PREFIX, PRECMD_PREFIX_LEN) == 0)
#define CMD_PREFIX "cmd_"
#define CMD_PREFIX_LEN 4
#define WANT_CMD() \
(std::strncmp(p, CMD_PREFIX, CMD_PREFIX_LEN) == 0)
#define DIR_PREFIX "dir_"
#define DIR_PREFIX_LEN 4
#define WANT_DIR() \
(std::strncmp(p, DIR_PREFIX, DIR_PREFIX_LEN) == 0)
bool process_option(const string& whence, const string& name, scope_t& scope,
const char * arg, const string& varname);

View file

@ -271,8 +271,12 @@ namespace {
}
}
expr_t::ptr_op_t post_t::lookup(const string& name)
expr_t::ptr_op_t post_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
if (kind != symbol_t::FUNCTION)
return item_t::lookup(kind, name);
switch (name[0]) {
case 'a':
if (name[1] == '\0' || name == "amount")
@ -366,7 +370,7 @@ expr_t::ptr_op_t post_t::lookup(const string& name)
break;
}
return item_t::lookup(name);
return item_t::lookup(kind, name);
}
bool post_t::valid() const

View file

@ -119,7 +119,8 @@ public:
return ! has_flags(POST_VIRTUAL) || has_flags(POST_MUST_BALANCE);
}
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
bool valid() const;

View file

@ -51,7 +51,7 @@ namespace {
{
if (value.is_scope()) {
if (scope_t * scope = value.as_scope())
return expr_t(scope->lookup(name), scope);
return expr_t(scope->lookup(symbol_t::FUNCTION, name), scope);
}
throw_(value_error, _("Cannot lookup attributes in %1") << value.label());
return expr_t();

View file

@ -285,38 +285,40 @@ python_interpreter_t::lookup_option(const char * p)
return NULL;
}
expr_t::ptr_op_t python_interpreter_t::lookup(const string& name)
expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
// Give our superclass first dibs on symbol definitions
if (expr_t::ptr_op_t op = session_t::lookup(name))
if (expr_t::ptr_op_t op = session_t::lookup(kind, name))
return op;
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
if (option_t<python_interpreter_t> * handler = lookup_option(q))
return MAKE_OPT_HANDLER(python_interpreter_t, handler);
switch (kind) {
case symbol_t::FUNCTION:
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(name, obj));
}
break;
case 'p':
if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
switch (*q) {
case 'p':
if (is_eq(q, "python"))
return MAKE_FUNCTOR(python_interpreter_t::python_command);
break;
}
}
case symbol_t::OPTION:
if (option_t<python_interpreter_t> * handler = lookup_option(name.c_str()))
return MAKE_OPT_HANDLER(python_interpreter_t, handler);
break;
case symbol_t::PRECOMMAND: {
const char * p = name.c_str();
switch (*p) {
case 'p':
if (is_eq(p, "python"))
return MAKE_FUNCTOR(python_interpreter_t::python_command);
break;
}
}
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(name, obj));
default:
break;
}
return NULL;

View file

@ -103,7 +103,8 @@ public:
option_t<python_interpreter_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
#if BOOST_VERSION >= 103700
OPTION_(python_interpreter_t, import_, DO_(scope) {

View file

@ -464,8 +464,8 @@ value_t report_t::echo_command(call_scope_t& scope)
bool report_t::maybe_import(const string& module)
{
if (lookup(string(OPT_PREFIX) + "import_")) {
expr_t(string(OPT_PREFIX) + "import_(\"" + module + "\")").calc(*this);
if (lookup(symbol_t::OPTION, "import_")) {
expr_t(string("import_(\"") + module + "\")").calc(*this);
return true;
}
return false;
@ -707,304 +707,316 @@ option_t<report_t> * report_t::lookup_option(const char * p)
return NULL;
}
void report_t::define(const string& name, expr_t::ptr_op_t def)
void report_t::define(const symbol_t::kind_t kind, const string& name,
expr_t::ptr_op_t def)
{
session.define(name, def);
session.define(kind, name, def);
}
expr_t::ptr_op_t report_t::lookup(const string& name)
expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
if (expr_t::ptr_op_t def = session.lookup(name))
if (expr_t::ptr_op_t def = session.lookup(kind, name))
return def;
const char * p = name.c_str();
switch (*p) {
case 'a':
if (is_eq(p, "amount_expr"))
return MAKE_FUNCTOR(report_t::fn_amount_expr);
else if (is_eq(p, "ansify_if"))
return MAKE_FUNCTOR(report_t::fn_ansify_if);
else if (is_eq(p, "abs"))
return MAKE_FUNCTOR(report_t::fn_abs);
switch (kind) {
case symbol_t::FUNCTION:
switch (*p) {
case 'a':
if (is_eq(p, "amount_expr"))
return MAKE_FUNCTOR(report_t::fn_amount_expr);
else if (is_eq(p, "ansify_if"))
return MAKE_FUNCTOR(report_t::fn_ansify_if);
else if (is_eq(p, "abs"))
return MAKE_FUNCTOR(report_t::fn_abs);
break;
case 'b':
if (is_eq(p, "black"))
return WRAP_FUNCTOR(fn_black);
else if (is_eq(p, "blink"))
return WRAP_FUNCTOR(fn_blink);
else if (is_eq(p, "blue"))
return WRAP_FUNCTOR(fn_blue);
else if (is_eq(p, "bold"))
return WRAP_FUNCTOR(fn_bold);
break;
case 'c':
if (is_eq(p, "cyan"))
return WRAP_FUNCTOR(fn_cyan);
break;
case 'd':
if (is_eq(p, "display_amount"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
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);
break;
case 'f':
if (is_eq(p, "format_date"))
return MAKE_FUNCTOR(report_t::fn_format_date);
break;
case 'g':
if (is_eq(p, "get_at"))
return MAKE_FUNCTOR(report_t::fn_get_at);
else if (is_eq(p, "green"))
return WRAP_FUNCTOR(fn_green);
break;
case 'i':
if (is_eq(p, "is_seq"))
return MAKE_FUNCTOR(report_t::fn_is_seq);
break;
case 'j':
if (is_eq(p, "justify"))
return MAKE_FUNCTOR(report_t::fn_justify);
else if (is_eq(p, "join"))
return MAKE_FUNCTOR(report_t::fn_join);
break;
case 'm':
if (is_eq(p, "market"))
return MAKE_FUNCTOR(report_t::fn_market);
else if (is_eq(p, "magenta"))
return WRAP_FUNCTOR(fn_magenta);
break;
case 'n':
if (is_eq(p, "null"))
return WRAP_FUNCTOR(fn_null);
else if (is_eq(p, "now"))
return MAKE_FUNCTOR(report_t::fn_now);
break;
case 'o':
if (is_eq(p, "options"))
return MAKE_FUNCTOR(report_t::fn_options);
break;
case 'p':
if (is_eq(p, "post"))
return WRAP_FUNCTOR(fn_false);
else if (is_eq(p, "percent"))
return MAKE_FUNCTOR(report_t::fn_percent);
else if (is_eq(p, "price"))
return MAKE_FUNCTOR(report_t::fn_price);
break;
case 'q':
if (is_eq(p, "quoted"))
return MAKE_FUNCTOR(report_t::fn_quoted);
else if (is_eq(p, "quantity"))
return MAKE_FUNCTOR(report_t::fn_quantity);
break;
case 'r':
if (is_eq(p, "rounded"))
return MAKE_FUNCTOR(report_t::fn_rounded);
else if (is_eq(p, "red"))
return WRAP_FUNCTOR(fn_red);
break;
case 's':
if (is_eq(p, "scrub"))
return MAKE_FUNCTOR(report_t::fn_scrub);
else if (is_eq(p, "strip"))
return MAKE_FUNCTOR(report_t::fn_strip);
break;
case 't':
if (is_eq(p, "truncated"))
return MAKE_FUNCTOR(report_t::fn_truncated);
else if (is_eq(p, "total_expr"))
return MAKE_FUNCTOR(report_t::fn_total_expr);
else if (is_eq(p, "today"))
return MAKE_FUNCTOR(report_t::fn_today);
else if (is_eq(p, "t"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
break;
case 'T':
if (is_eq(p, "T"))
return MAKE_FUNCTOR(report_t::fn_display_total);
break;
case 'u':
if (is_eq(p, "underline"))
return WRAP_FUNCTOR(fn_underline);
else if (is_eq(p, "unrounded"))
return MAKE_FUNCTOR(report_t::fn_unrounded);
break;
case 'w':
if (is_eq(p, "white"))
return WRAP_FUNCTOR(fn_white);
break;
case 'y':
if (is_eq(p, "yellow"))
return WRAP_FUNCTOR(fn_yellow);
break;
}
// Check if they are trying to access an option's setting or value.
if (option_t<report_t> * handler = lookup_option(p))
return MAKE_OPT_FUNCTOR(report_t, handler);
break;
case 'b':
if (is_eq(p, "black"))
return WRAP_FUNCTOR(fn_black);
else if (is_eq(p, "blink"))
return WRAP_FUNCTOR(fn_blink);
else if (is_eq(p, "blue"))
return WRAP_FUNCTOR(fn_blue);
else if (is_eq(p, "bold"))
return WRAP_FUNCTOR(fn_bold);
case symbol_t::OPTION:
if (option_t<report_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(report_t, handler);
break;
case 'c':
if (WANT_CMD()) { const char * q = p + CMD_PREFIX_LEN;
switch (*q) {
case 'b':
if (*(q + 1) == '\0' || is_eq(q, "bal") || is_eq(q, "balance")) {
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(balance_format_))),
*this, "#balance"));
}
else if (is_eq(q, "budget")) {
HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)");
budget_flags |= BUDGET_WRAP_VALUES;
if (! (budget_flags & ~BUDGET_WRAP_VALUES))
budget_flags |= BUDGET_BUDGETED;
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(budget_format_))),
*this, "#budget"));
}
break;
case 'c':
if (is_eq(q, "csv")) {
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(csv_format_))),
*this, "#csv"));
}
else if (is_eq(q, "cleared")) {
HANDLER(amount_).set_expr(string("#cleared"),
"(amount, cleared ? amount : 0)");
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(cleared_format_))),
*this, "#cleared"));
}
break;
case 'e':
if (is_eq(q, "equity"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_))),
*this, "#equity"));
else if (is_eq(q, "entry"))
return WRAP_FUNCTOR(xact_command);
else if (is_eq(q, "emacs"))
return WRAP_FUNCTOR
(reporter<>(new format_emacs_posts(output_stream), *this, "#emacs"));
else if (is_eq(q, "echo"))
return MAKE_FUNCTOR(report_t::echo_command);
break;
case 'p':
if (*(q + 1) == '\0' || is_eq(q, "print"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_)),
HANDLED(raw)), *this, "#print"));
else if (is_eq(q, "prices"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(prices_format_))),
*this, "#prices"));
else if (is_eq(q, "pricesdb"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))),
*this, "#pricesdb"));
else if (is_eq(q, "python") && maybe_import("ledger.interp"))
return session.lookup(string(CMD_PREFIX) + "python");
break;
case 'r':
if (*(q + 1) == '\0' || is_eq(q, "reg") || is_eq(q, "register"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(register_format_))),
*this, "#register"));
else if (is_eq(q, "reload"))
return MAKE_FUNCTOR(report_t::reload_command);
break;
case 's':
if (is_eq(q, "stats") || is_eq(q, "stat"))
return WRAP_FUNCTOR(report_statistics);
else
if (is_eq(q, "server") && maybe_import("ledger.server"))
return session.lookup(string(CMD_PREFIX) + "server");
break;
case 'x':
if (is_eq(q, "xact"))
return WRAP_FUNCTOR(xact_command);
break;
case symbol_t::COMMAND:
switch (*p) {
case 'b':
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) {
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(balance_format_))),
*this, "#balance"));
}
}
else if (is_eq(p, "cyan"))
return WRAP_FUNCTOR(fn_cyan);
break;
else if (is_eq(p, "budget")) {
HANDLER(amount_).set_expr(string("#budget"), "(amount, 0)");
case 'd':
if (is_eq(p, "display_amount"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
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);
break;
budget_flags |= BUDGET_WRAP_VALUES;
if (! (budget_flags & ~BUDGET_WRAP_VALUES))
budget_flags |= BUDGET_BUDGETED;
case 'f':
if (is_eq(p, "format_date"))
return MAKE_FUNCTOR(report_t::fn_format_date);
break;
case 'g':
if (is_eq(p, "get_at"))
return MAKE_FUNCTOR(report_t::fn_get_at);
else if (is_eq(p, "green"))
return WRAP_FUNCTOR(fn_green);
break;
case 'i':
if (is_eq(p, "is_seq"))
return MAKE_FUNCTOR(report_t::fn_is_seq);
break;
case 'j':
if (is_eq(p, "justify"))
return MAKE_FUNCTOR(report_t::fn_justify);
else if (is_eq(p, "join"))
return MAKE_FUNCTOR(report_t::fn_join);
break;
case 'm':
if (is_eq(p, "market"))
return MAKE_FUNCTOR(report_t::fn_market);
else if (is_eq(p, "magenta"))
return WRAP_FUNCTOR(fn_magenta);
break;
case 'n':
if (is_eq(p, "null"))
return WRAP_FUNCTOR(fn_null);
else if (is_eq(p, "now"))
return MAKE_FUNCTOR(report_t::fn_now);
break;
case 'o':
if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
if (option_t<report_t> * handler = lookup_option(q))
return MAKE_OPT_HANDLER(report_t, handler);
}
else if (is_eq(p, "options")) {
return MAKE_FUNCTOR(report_t::fn_options);
}
break;
case 'p':
if (WANT_PRECMD()) { const char * q = p + PRECMD_PREFIX_LEN;
switch (*q) {
case 'a':
if (is_eq(q, "args"))
return WRAP_FUNCTOR(args_command);
break;
case 'e':
if (is_eq(q, "eval"))
return WRAP_FUNCTOR(eval_command);
break;
case 'f':
if (is_eq(q, "format"))
return WRAP_FUNCTOR(format_command);
break;
case 'g':
if (is_eq(q, "generate"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::generate_report>
(new format_posts(*this, report_format(HANDLER(print_format_)),
false), *this, "#generate"));
case 'h':
if (is_eq(q, "hello") && maybe_import("ledger.hello"))
return session.lookup(string(PRECMD_PREFIX) + "hello");
break;
case 'p':
if (is_eq(q, "parse"))
return WRAP_FUNCTOR(parse_command);
else if (is_eq(q, "period"))
return WRAP_FUNCTOR(period_command);
break;
case 't':
if (is_eq(q, "template"))
return WRAP_FUNCTOR(template_command);
break;
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(budget_format_))),
*this, "#budget"));
}
break;
case 'c':
if (is_eq(p, "csv")) {
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(csv_format_))),
*this, "#csv"));
}
else if (is_eq(p, "cleared")) {
HANDLER(amount_).set_expr(string("#cleared"),
"(amount, cleared ? amount : 0)");
return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(cleared_format_))),
*this, "#cleared"));
}
break;
case 'e':
if (is_eq(p, "equity"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_))),
*this, "#equity"));
else if (is_eq(p, "entry"))
return WRAP_FUNCTOR(xact_command);
else if (is_eq(p, "emacs"))
return WRAP_FUNCTOR
(reporter<>(new format_emacs_posts(output_stream), *this, "#emacs"));
else if (is_eq(p, "echo"))
return MAKE_FUNCTOR(report_t::echo_command);
break;
case 'p':
if (*(p + 1) == '\0' || is_eq(p, "print"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(print_format_)),
HANDLED(raw)), *this, "#print"));
else if (is_eq(p, "prices"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(prices_format_))),
*this, "#prices"));
else if (is_eq(p, "pricesdb"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))),
*this, "#pricesdb"));
else if (is_eq(p, "python") && maybe_import("ledger.interp"))
return session.lookup(symbol_t::COMMAND, "python");
break;
case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register"))
return WRAP_FUNCTOR
(reporter<>
(new format_posts(*this, report_format(HANDLER(register_format_))),
*this, "#register"));
else if (is_eq(p, "reload"))
return MAKE_FUNCTOR(report_t::reload_command);
break;
case 's':
if (is_eq(p, "stats") || is_eq(p, "stat"))
return WRAP_FUNCTOR(report_statistics);
else
if (is_eq(p, "server") && maybe_import("ledger.server"))
return session.lookup(symbol_t::COMMAND, "server");
break;
case 'x':
if (is_eq(p, "xact"))
return WRAP_FUNCTOR(xact_command);
break;
}
else if (is_eq(p, "post"))
return WRAP_FUNCTOR(fn_false);
else if (is_eq(p, "percent"))
return MAKE_FUNCTOR(report_t::fn_percent);
else if (is_eq(p, "price"))
return MAKE_FUNCTOR(report_t::fn_price);
break;
case 'q':
if (is_eq(p, "quoted"))
return MAKE_FUNCTOR(report_t::fn_quoted);
else if (is_eq(p, "quantity"))
return MAKE_FUNCTOR(report_t::fn_quantity);
case symbol_t::PRECOMMAND:
switch (*p) {
case 'a':
if (is_eq(p, "args"))
return WRAP_FUNCTOR(args_command);
break;
case 'e':
if (is_eq(p, "eval"))
return WRAP_FUNCTOR(eval_command);
break;
case 'f':
if (is_eq(p, "format"))
return WRAP_FUNCTOR(format_command);
break;
case 'g':
if (is_eq(p, "generate"))
return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::generate_report>
(new format_posts(*this, report_format(HANDLER(print_format_)),
false), *this, "#generate"));
case 'h':
if (is_eq(p, "hello") && maybe_import("ledger.hello"))
return session.lookup(symbol_t::PRECOMMAND, "hello");
break;
case 'p':
if (is_eq(p, "parse"))
return WRAP_FUNCTOR(parse_command);
else if (is_eq(p, "period"))
return WRAP_FUNCTOR(period_command);
break;
case 't':
if (is_eq(p, "template"))
return WRAP_FUNCTOR(template_command);
break;
}
break;
case 'r':
if (is_eq(p, "rounded"))
return MAKE_FUNCTOR(report_t::fn_rounded);
else if (is_eq(p, "red"))
return WRAP_FUNCTOR(fn_red);
break;
case 's':
if (is_eq(p, "scrub"))
return MAKE_FUNCTOR(report_t::fn_scrub);
else if (is_eq(p, "strip"))
return MAKE_FUNCTOR(report_t::fn_strip);
break;
case 't':
if (is_eq(p, "truncated"))
return MAKE_FUNCTOR(report_t::fn_truncated);
else if (is_eq(p, "total_expr"))
return MAKE_FUNCTOR(report_t::fn_total_expr);
else if (is_eq(p, "today"))
return MAKE_FUNCTOR(report_t::fn_today);
else if (is_eq(p, "t"))
return MAKE_FUNCTOR(report_t::fn_display_amount);
break;
case 'T':
if (is_eq(p, "T"))
return MAKE_FUNCTOR(report_t::fn_display_total);
break;
case 'u':
if (is_eq(p, "underline"))
return WRAP_FUNCTOR(fn_underline);
else if (is_eq(p, "unrounded"))
return MAKE_FUNCTOR(report_t::fn_unrounded);
break;
case 'w':
if (is_eq(p, "white"))
return WRAP_FUNCTOR(fn_white);
break;
case 'y':
if (is_eq(p, "yellow"))
return WRAP_FUNCTOR(fn_yellow);
default:
break;
}
// Check if they are trying to access an option's setting or value.
if (option_t<report_t> * handler = lookup_option(p))
return MAKE_OPT_FUNCTOR(report_t, handler);
return NULL;
}

View file

@ -311,9 +311,11 @@ public:
option_t<report_t> * lookup_option(const char * p);
virtual void define(const string& name, expr_t::ptr_op_t def);
virtual void define(const symbol_t::kind_t kind, const string& name,
expr_t::ptr_op_t def);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
/**
* Option handlers

View file

@ -35,31 +35,34 @@
namespace ledger {
void symbol_scope_t::define(const string& name, expr_t::ptr_op_t def)
void symbol_scope_t::define(const symbol_t::kind_t kind,
const string& name, expr_t::ptr_op_t def)
{
DEBUG("scope.symbols", "Defining '" << name << "' = " << def);
std::pair<symbol_map::iterator, bool> result
= symbols.insert(symbol_map::value_type(name, def));
= symbols.insert(symbol_map::value_type(symbol_t(kind, name, def), def));
if (! result.second) {
symbol_map::iterator i = symbols.find(name);
symbol_map::iterator i = symbols.find(symbol_t(kind, 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 '%1' in same scope") << name);
result = symbols.insert(symbol_map::value_type(symbol_t(kind, name, def),
def));
if (! result.second)
throw_(compile_error,
_("Redefinition of '%1' in the same scope") << name);
}
}
expr_t::ptr_op_t symbol_scope_t::lookup(const string& name)
expr_t::ptr_op_t symbol_scope_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
symbol_map::const_iterator i = symbols.find(name);
symbol_map::const_iterator i = symbols.find(symbol_t(kind, name));
if (i != symbols.end())
return (*i).second;
return child_scope_t::lookup(name);
return child_scope_t::lookup(kind, name);
}
} // namespace ledger

View file

@ -50,6 +50,61 @@
namespace ledger {
/**
* @brief Brief
*
* Long.
*/
struct symbol_t
{
enum kind_t {
UNKNOWN,
FUNCTION,
OPTION,
PRECOMMAND,
COMMAND,
DIRECTIVE
};
kind_t kind;
string name;
expr_t::ptr_op_t definition;
symbol_t() : kind(UNKNOWN), name(""), definition(NULL) {
TRACE_CTOR(symbol_t, "");
}
symbol_t(kind_t _kind, string _name, expr_t::ptr_op_t _definition = NULL)
: kind(_kind), name(_name), definition(_definition) {
TRACE_CTOR(symbol_t, "symbol_t::kind_t, string");
}
symbol_t(const symbol_t& sym)
: kind(sym.kind), name(sym.name),
definition(sym.definition) {
TRACE_CTOR(symbol_t, "copy");
}
~symbol_t() throw() {
TRACE_DTOR(symbol_t);
}
bool operator<(const symbol_t& sym) const {
return kind < sym.kind || name < sym.name;
}
#if defined(HAVE_BOOST_SERIALIZATION)
private:
/** Serialization. */
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /* version */) {
ar & kind;
ar & name;
ar & definition;
}
#endif // HAVE_BOOST_SERIALIZATION
};
/**
* @brief Brief
*
@ -65,8 +120,10 @@ public:
TRACE_DTOR(scope_t);
}
virtual void define(const string&, expr_t::ptr_op_t) {}
virtual expr_t::ptr_op_t lookup(const string& name) = 0;
virtual void define(const symbol_t::kind_t, const string&,
expr_t::ptr_op_t) {}
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name) = 0;
#if defined(HAVE_BOOST_SERIALIZATION)
private:
@ -100,14 +157,16 @@ public:
TRACE_DTOR(child_scope_t);
}
virtual void define(const string& name, expr_t::ptr_op_t def) {
virtual void define(const symbol_t::kind_t kind,
const string& name, expr_t::ptr_op_t def) {
if (parent)
parent->define(name, def);
parent->define(kind, name, def);
}
virtual expr_t::ptr_op_t lookup(const string& name) {
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name) {
if (parent)
return parent->lookup(name);
return parent->lookup(kind, name);
return NULL;
}
@ -132,7 +191,7 @@ private:
*/
class symbol_scope_t : public child_scope_t
{
typedef std::map<const string, expr_t::ptr_op_t> symbol_map;
typedef std::map<symbol_t, expr_t::ptr_op_t> symbol_map;
symbol_map symbols;
@ -147,9 +206,11 @@ public:
TRACE_DTOR(symbol_scope_t);
}
virtual void define(const string& name, expr_t::ptr_op_t def);
virtual void define(const symbol_t::kind_t kind, const string& name,
expr_t::ptr_op_t def);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
#if defined(HAVE_BOOST_SERIALIZATION)
private:
@ -259,15 +320,17 @@ public:
TRACE_DTOR(bind_scope_t);
}
virtual void define(const string& name, expr_t::ptr_op_t def) {
parent->define(name, def);
grandchild.define(name, def);
virtual void define(const symbol_t::kind_t kind, const string& name,
expr_t::ptr_op_t def) {
parent->define(kind, name, def);
grandchild.define(kind, name, def);
}
virtual expr_t::ptr_op_t lookup(const string& name) {
if (expr_t::ptr_op_t def = grandchild.lookup(name))
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name) {
if (expr_t::ptr_op_t def = grandchild.lookup(kind, name))
return def;
return child_scope_t::lookup(name);
return child_scope_t::lookup(kind, name);
}
#if defined(HAVE_BOOST_SERIALIZATION)

View file

@ -284,23 +284,26 @@ option_t<session_t> * session_t::lookup_option(const char * p)
return NULL;
}
expr_t::ptr_op_t session_t::lookup(const string& name)
expr_t::ptr_op_t session_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
const char * p = name.c_str();
switch (*p) {
case 'o':
if (WANT_OPT()) { p += OPT_PREFIX_LEN;
if (option_t<session_t> * handler = lookup_option(p))
return MAKE_OPT_HANDLER(session_t, handler);
}
switch (kind) {
case symbol_t::FUNCTION:
// Check if they are trying to access an option's setting or value.
if (option_t<session_t> * handler = lookup_option(name.c_str()))
return MAKE_OPT_FUNCTOR(session_t, handler);
break;
case symbol_t::OPTION:
if (option_t<session_t> * handler = lookup_option(name.c_str()))
return MAKE_OPT_HANDLER(session_t, handler);
break;
default:
break;
}
// Check if they are trying to access an option's setting or value.
if (option_t<session_t> * handler = lookup_option(p))
return MAKE_OPT_FUNCTOR(session_t, handler);
return symbol_scope_t::lookup(name);
return symbol_scope_t::lookup(kind, name);
}
} // namespace ledger

View file

@ -116,7 +116,8 @@ public:
option_t<session_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
/**
* Option handlers

View file

@ -140,7 +140,8 @@ namespace {
std::streamsize len,
account_t * account);
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
};
void parse_amount_expr(scope_t& scope,
@ -749,11 +750,7 @@ void instance_t::general_directive(char * line)
break;
}
scoped_array<char> directive(new char[std::strlen(p) + DIR_PREFIX_LEN + 1]);
std::strcpy(directive.get(), DIR_PREFIX);
std::strcpy(directive.get() + DIR_PREFIX_LEN, p);
if (expr_t::ptr_op_t op = lookup(directive.get())) {
if (expr_t::ptr_op_t op = lookup(symbol_t::DIRECTIVE, p)) {
call_scope_t args(*this);
args.push_back(string_value(p));
op->as_function()(args);
@ -1233,9 +1230,10 @@ xact_t * instance_t::parse_xact(char * line,
}
}
expr_t::ptr_op_t instance_t::lookup(const string& name)
expr_t::ptr_op_t instance_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
return scope.lookup(name);
return scope.lookup(kind, name);
}
std::size_t journal_t::parse(std::istream& in,

View file

@ -422,8 +422,12 @@ namespace {
}
}
expr_t::ptr_op_t xact_t::lookup(const string& name)
expr_t::ptr_op_t xact_t::lookup(const symbol_t::kind_t kind,
const string& name)
{
if (kind != symbol_t::FUNCTION)
return item_t::lookup(kind, name);
switch (name[0]) {
case 'c':
if (name == "code")
@ -448,7 +452,7 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
break;
}
return item_t::lookup(name);
return item_t::lookup(kind, name);
}
bool xact_t::valid() const

View file

@ -123,7 +123,8 @@ public:
string idstring() const;
string id() const;
virtual expr_t::ptr_op_t lookup(const string& name);
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name);
virtual bool valid() const;