Adding support for recursive aliases.
Alias expansion is now a loop. If you define alias A=B:A alias B=C:B then A will expand to C:B:A. Also added a short section to the manual about this.
This commit is contained in:
parent
bc08eed3cb
commit
c80b495546
4 changed files with 74 additions and 20 deletions
|
|
@ -2158,9 +2158,22 @@ alias Checking=Assets:Credit Union:Joint Checking Account
|
||||||
@end smallexample
|
@end smallexample
|
||||||
|
|
||||||
The aliases are only in effect for transactions read in after the alias
|
The aliases are only in effect for transactions read in after the alias
|
||||||
is defined and are effected by @code{account} directives that precede
|
is defined and are affected by @code{account} directives that precede
|
||||||
them.
|
them.
|
||||||
|
|
||||||
|
Aliases can refer to other aliases, the following example produces exactly
|
||||||
|
the same accounts as the preceding one:
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
alias Entertainment=Expenses:Entertainment
|
||||||
|
alias Dining=Entertainment:Dining
|
||||||
|
alias Checking=Assets:Credit Union:Joint Checking Account
|
||||||
|
|
||||||
|
2011/11/30 ChopChop
|
||||||
|
Dining $10.00
|
||||||
|
Checking
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
@item assert
|
@item assert
|
||||||
@c instance_t::assert_directive
|
@c instance_t::assert_directive
|
||||||
An assertion can throw an error if a condition is not met during
|
An assertion can throw an error if a condition is not met during
|
||||||
|
|
|
||||||
|
|
@ -121,26 +121,9 @@ account_t * journal_t::find_account_re(const string& regexp)
|
||||||
account_t * journal_t::register_account(const string& name, post_t * post,
|
account_t * journal_t::register_account(const string& name, post_t * post,
|
||||||
account_t * master_account)
|
account_t * master_account)
|
||||||
{
|
{
|
||||||
account_t * result = NULL;
|
// If there are any account aliases, substitute before creating an account
|
||||||
|
|
||||||
// If there any account aliases, substitute before creating an account
|
|
||||||
// object.
|
// object.
|
||||||
if (account_aliases.size() > 0) {
|
account_t * result = expand_aliases(name);
|
||||||
accounts_map::const_iterator i = account_aliases.find(name);
|
|
||||||
if (i != account_aliases.end()) {
|
|
||||||
result = (*i).second;
|
|
||||||
} else {
|
|
||||||
// only check the very first account for alias expansion, in case
|
|
||||||
// that can be expanded successfully
|
|
||||||
size_t colon = name.find(':');
|
|
||||||
if(colon != string::npos) {
|
|
||||||
accounts_map::const_iterator j = account_aliases.find(name.substr(0, colon));
|
|
||||||
if (j != account_aliases.end()) {
|
|
||||||
result = find_account((*j).second->fullname() + name.substr(colon));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the account object and associate it with the journal; this
|
// Create the account object and associate it with the journal; this
|
||||||
// is registering the account.
|
// is registering the account.
|
||||||
|
|
@ -178,7 +161,58 @@ account_t * journal_t::register_account(const string& name, post_t * post,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
account_t * journal_t::expand_aliases(string name) {
|
||||||
|
// Aliases are expanded recursively, so if both alias Foo=Bar:Foo and
|
||||||
|
// alias Bar=Baaz:Bar are in effect, first Foo will be expanded to Bar:Foo,
|
||||||
|
// then Bar:Foo will be expanded to Baaz:Bar:Foo.
|
||||||
|
// The expansion loop keeps a list of already expanded names in order to
|
||||||
|
// prevent infinite excursion. Each alias may only be expanded at most once.
|
||||||
|
account_t * result = NULL;
|
||||||
|
|
||||||
|
bool keep_expanding = true;
|
||||||
|
std::list<string> already_seen;
|
||||||
|
// loop until no expansion can be found
|
||||||
|
while(keep_expanding) {
|
||||||
|
if (account_aliases.size() > 0) {
|
||||||
|
accounts_map::const_iterator i = account_aliases.find(name);
|
||||||
|
if (i != account_aliases.end()) {
|
||||||
|
if(std::find(already_seen.begin(), already_seen.end(), name) != already_seen.end()) {
|
||||||
|
throw_(std::runtime_error,
|
||||||
|
_f("Infinite recursion on alias expansion for %1%")
|
||||||
|
% name);
|
||||||
|
}
|
||||||
|
// there is an alias for the full account name, including colons
|
||||||
|
already_seen.push_back(name);
|
||||||
|
result = (*i).second;
|
||||||
|
name = result->fullname();
|
||||||
|
} else {
|
||||||
|
// only check the very first account for alias expansion, in case
|
||||||
|
// that can be expanded successfully
|
||||||
|
size_t colon = name.find(':');
|
||||||
|
if(colon != string::npos) {
|
||||||
|
string first_account_name = name.substr(0, colon);
|
||||||
|
accounts_map::const_iterator j = account_aliases.find(first_account_name);
|
||||||
|
if (j != account_aliases.end()) {
|
||||||
|
if(std::find(already_seen.begin(), already_seen.end(), first_account_name) != already_seen.end()) {
|
||||||
|
throw_(std::runtime_error,
|
||||||
|
_f("Infinite recursion on alias expansion for %1%")
|
||||||
|
% first_account_name);
|
||||||
|
}
|
||||||
|
already_seen.push_back(first_account_name);
|
||||||
|
result = find_account((*j).second->fullname() + name.substr(colon));
|
||||||
|
name = result->fullname();
|
||||||
|
} else {
|
||||||
|
keep_expanding = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
keep_expanding = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,8 @@ public:
|
||||||
account_t * find_account(const string& name, bool auto_create = true);
|
account_t * find_account(const string& name, bool auto_create = true);
|
||||||
account_t * find_account_re(const string& regexp);
|
account_t * find_account_re(const string& regexp);
|
||||||
|
|
||||||
|
account_t * expand_aliases(string name);
|
||||||
|
|
||||||
account_t * register_account(const string& name, post_t * post,
|
account_t * register_account(const string& name, post_t * post,
|
||||||
account_t * master = NULL);
|
account_t * master = NULL);
|
||||||
string register_payee(const string& name, xact_t * xact);
|
string register_payee(const string& name, xact_t * xact);
|
||||||
|
|
|
||||||
|
|
@ -977,6 +977,11 @@ void instance_t::account_alias_directive(account_t * account, string alias)
|
||||||
// (account), add a reference to the account in the `account_aliases'
|
// (account), add a reference to the account in the `account_aliases'
|
||||||
// map, which is used by the post parser to resolve alias references.
|
// map, which is used by the post parser to resolve alias references.
|
||||||
trim(alias);
|
trim(alias);
|
||||||
|
// Ensure that no alias like "alias Foo=Foo" is registered.
|
||||||
|
if ( alias == account->fullname()) {
|
||||||
|
throw_(parse_error, _f("Illegal alias %1%=%2%")
|
||||||
|
% alias % account->fullname());
|
||||||
|
}
|
||||||
std::pair<accounts_map::iterator, bool> result =
|
std::pair<accounts_map::iterator, bool> result =
|
||||||
context.journal->account_aliases.insert
|
context.journal->account_aliases.insert
|
||||||
(accounts_map::value_type(alias, account));
|
(accounts_map::value_type(alias, account));
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue