Implemented account and commodity directives
This commit is contained in:
parent
1d89093059
commit
0e7b4fb182
2 changed files with 208 additions and 104 deletions
279
src/textual.cc
279
src/textual.cc
|
|
@ -140,18 +140,23 @@ namespace {
|
||||||
bool general_directive(char * line);
|
bool general_directive(char * line);
|
||||||
|
|
||||||
void account_directive(char * line);
|
void account_directive(char * line);
|
||||||
void account_alias_directive(char * line);
|
void account_alias_directive(account_t * account, string alias);
|
||||||
void account_payee_directive(char * line);
|
void account_payee_directive(account_t * account, string payee);
|
||||||
|
void account_default_directive(account_t * account);
|
||||||
|
|
||||||
|
void default_account_directive(char * line);
|
||||||
|
void alias_directive(char * line);
|
||||||
|
|
||||||
void payee_directive(char * line);
|
void payee_directive(char * line);
|
||||||
void payee_alias_directive(char * line);
|
void payee_alias_directive(const string& payee, string alias);
|
||||||
|
|
||||||
void commodity_directive(char * line);
|
void commodity_directive(char * line);
|
||||||
#if 0
|
void commodity_alias_directive(commodity_t& comm, string alias);
|
||||||
void commodity_alias_directive(char * line);
|
void commodity_format_directive(commodity_t& comm, string format);
|
||||||
void commodity_format_directive(char * line);
|
void commodity_nomarket_directive(commodity_t& comm);
|
||||||
void commodity_nomarket_directive(char * line);
|
void commodity_default_directive(commodity_t& comm);
|
||||||
#endif
|
|
||||||
|
void default_commodity_directive(char * line);
|
||||||
|
|
||||||
void apply_directive(char * line);
|
void apply_directive(char * line);
|
||||||
void apply_account_directive(char * line);
|
void apply_account_directive(char * line);
|
||||||
|
|
@ -167,16 +172,13 @@ namespace {
|
||||||
void price_conversion_directive(char * line);
|
void price_conversion_directive(char * line);
|
||||||
void nomarket_directive(char * line);
|
void nomarket_directive(char * line);
|
||||||
|
|
||||||
void default_account_directive(char * line);
|
|
||||||
void default_commodity_directive(char * line);
|
|
||||||
|
|
||||||
void include_directive(char * line);
|
void include_directive(char * line);
|
||||||
void option_directive(char * line);
|
void option_directive(char * line);
|
||||||
void define_directive(char * line);
|
void comment_directive(char * line);
|
||||||
void expr_directive(char * line);
|
|
||||||
|
void eval_directive(char * line);
|
||||||
void assert_directive(char * line);
|
void assert_directive(char * line);
|
||||||
void check_directive(char * line);
|
void check_directive(char * line);
|
||||||
void comment_directive(char * line);
|
|
||||||
|
|
||||||
post_t * parse_post(char * line,
|
post_t * parse_post(char * line,
|
||||||
std::streamsize len,
|
std::streamsize len,
|
||||||
|
|
@ -598,7 +600,8 @@ void instance_t::automated_xact_directive(char * line)
|
||||||
(remlen > 6 && *p == 'c' &&
|
(remlen > 6 && *p == 'c' &&
|
||||||
std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) ||
|
std::strncmp(p, "check", 5) == 0 && std::isspace(p[5])) ||
|
||||||
(remlen > 5 && *p == 'e' &&
|
(remlen > 5 && *p == 'e' &&
|
||||||
std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4]))) {
|
((std::strncmp(p, "expr", 4) == 0 && std::isspace(p[4])) ||
|
||||||
|
(std::strncmp(p, "eval", 4) == 0 && std::isspace(p[4]))))) {
|
||||||
const char c = *p;
|
const char c = *p;
|
||||||
p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]);
|
p = skip_ws(&p[*p == 'a' ? 6 : (*p == 'c' ? 5 : 4)]);
|
||||||
if (! ae->check_exprs)
|
if (! ae->check_exprs)
|
||||||
|
|
@ -861,17 +864,89 @@ void instance_t::end_apply_directive(char * kind)
|
||||||
|
|
||||||
void instance_t::account_directive(char * line)
|
void instance_t::account_directive(char * line)
|
||||||
{
|
{
|
||||||
|
istream_pos_type beg_pos = line_beg_pos;
|
||||||
|
std::size_t beg_linenum = linenum;
|
||||||
|
|
||||||
char * p = skip_ws(line);
|
char * p = skip_ws(line);
|
||||||
//account_t * account =
|
account_t * account =
|
||||||
context.journal.register_account(p, NULL,
|
context.journal.register_account(p, NULL, file_context(pathname, linenum),
|
||||||
file_context(pathname, linenum),
|
|
||||||
context.top_account());
|
context.top_account());
|
||||||
|
std::auto_ptr<auto_xact_t> ae;
|
||||||
|
|
||||||
|
while (peek_whitespace_line()) {
|
||||||
|
read_line(line);
|
||||||
|
char * q = skip_ws(line);
|
||||||
|
if (! *q)
|
||||||
|
break;
|
||||||
|
|
||||||
|
char * b = next_element(q);
|
||||||
|
string keyword(q);
|
||||||
|
if (keyword == "alias") {
|
||||||
|
account_alias_directive(account, b);
|
||||||
|
}
|
||||||
|
else if (keyword == "payee") {
|
||||||
|
account_payee_directive(account, b);
|
||||||
|
}
|
||||||
|
else if (keyword == "default") {
|
||||||
|
account_default_directive(account);
|
||||||
|
}
|
||||||
|
else if (keyword == "assert" || keyword == "check") {
|
||||||
|
keep_details_t keeper(true, true, true);
|
||||||
|
expr_t expr(string("account == \"") + account->fullname() + "\"");
|
||||||
|
predicate_t pred(expr.get_op(), keeper);
|
||||||
|
|
||||||
|
if (! ae.get()) {
|
||||||
|
ae.reset(new auto_xact_t(pred));
|
||||||
|
|
||||||
|
ae->pos = position_t();
|
||||||
|
ae->pos->pathname = pathname;
|
||||||
|
ae->pos->beg_pos = beg_pos;
|
||||||
|
ae->pos->beg_line = beg_linenum;
|
||||||
|
ae->pos->sequence = context.sequence++;
|
||||||
|
ae->check_exprs = auto_xact_t::check_expr_list();
|
||||||
|
}
|
||||||
|
|
||||||
|
ae->check_exprs->push_back
|
||||||
|
(auto_xact_t::check_expr_pair(expr_t(b),
|
||||||
|
keyword == "assert" ?
|
||||||
|
auto_xact_t::EXPR_ASSERTION :
|
||||||
|
auto_xact_t::EXPR_CHECK));
|
||||||
|
}
|
||||||
|
else if (keyword == "eval" || keyword == "expr") {
|
||||||
|
bind_scope_t bound_scope(context.scope, *account);
|
||||||
|
expr_t(b).calc(bound_scope);
|
||||||
|
}
|
||||||
|
else if (keyword == "note") {
|
||||||
|
account->note = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ae.get()) {
|
||||||
|
context.journal.auto_xacts.push_back(ae.get());
|
||||||
|
|
||||||
|
ae->journal = &context.journal;
|
||||||
|
ae->pos->end_pos = in.tellg();
|
||||||
|
ae->pos->end_line = linenum;
|
||||||
|
|
||||||
|
ae.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::account_alias_directive(char * line)
|
void instance_t::account_alias_directive(account_t * account, string alias)
|
||||||
{
|
{
|
||||||
char * b = skip_ws(line);
|
// Once we have an alias name (alias) and the target account
|
||||||
#if 0
|
// (account), add a reference to the account in the `account_aliases'
|
||||||
|
// map, which is used by the post parser to resolve alias references.
|
||||||
|
trim(alias);
|
||||||
|
std::pair<accounts_map::iterator, bool> result
|
||||||
|
= context.journal
|
||||||
|
.account_aliases.insert(accounts_map::value_type(alias, account));
|
||||||
|
assert(result.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::alias_directive(char * line)
|
||||||
|
{
|
||||||
|
char * b = next_element(line);
|
||||||
if (char * e = std::strchr(b, '=')) {
|
if (char * e = std::strchr(b, '=')) {
|
||||||
char * z = e - 1;
|
char * z = e - 1;
|
||||||
while (std::isspace(*z))
|
while (std::isspace(*z))
|
||||||
|
|
@ -879,53 +954,47 @@ void instance_t::account_alias_directive(char * line)
|
||||||
*e++ = '\0';
|
*e++ = '\0';
|
||||||
e = skip_ws(e);
|
e = skip_ws(e);
|
||||||
|
|
||||||
// Once we have an alias name (b) and the target account
|
account_alias_directive(context.top_account()->find_account(e), b);
|
||||||
// name (e), add a reference to the account in the
|
|
||||||
// `account_aliases' map, which is used by the post
|
|
||||||
// parser to resolve alias references.
|
|
||||||
account_t * acct = context.top_account()->find_account(e);
|
|
||||||
std::pair<accounts_map::iterator, bool> result
|
|
||||||
= account_aliases.insert(accounts_map::value_type(b, acct));
|
|
||||||
assert(result.second);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::account_payee_directive(char * line)
|
void instance_t::account_payee_directive(account_t * account, string payee)
|
||||||
{
|
{
|
||||||
|
trim(payee);
|
||||||
|
context.journal.payees_for_unknown_accounts
|
||||||
|
.push_back(account_mapping_t(mask_t(payee), account));
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::account_default_directive(account_t * account)
|
||||||
|
{
|
||||||
|
context.journal.bucket = account;
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::payee_directive(char * line)
|
void instance_t::payee_directive(char * line)
|
||||||
{
|
{
|
||||||
context.journal.register_payee(line, NULL, file_context(pathname, linenum));
|
string payee = context.journal
|
||||||
}
|
.register_payee(line, NULL, file_context(pathname, linenum));
|
||||||
|
|
||||||
void instance_t::payee_alias_directive(char * line)
|
|
||||||
{
|
|
||||||
char * payee = skip_ws(line);
|
|
||||||
char * regex = next_element(payee, true);
|
|
||||||
|
|
||||||
if (regex)
|
|
||||||
context.journal.payee_mappings.push_back
|
|
||||||
(payee_mapping_t(mask_t(regex), payee));
|
|
||||||
|
|
||||||
while (peek_whitespace_line()) {
|
while (peek_whitespace_line()) {
|
||||||
#if defined(NO_ASSERTS)
|
|
||||||
read_line(line);
|
read_line(line);
|
||||||
#else
|
char * p = skip_ws(line);
|
||||||
std::streamsize len = read_line(line);
|
if (! *p)
|
||||||
assert(len > 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
regex = skip_ws(line);
|
|
||||||
if (! *regex)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
context.journal.payee_mappings.push_back
|
char * b = next_element(p);
|
||||||
(payee_mapping_t(mask_t(regex), payee));
|
string keyword(p);
|
||||||
|
if (keyword == "alias")
|
||||||
|
payee_alias_directive(payee, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void instance_t::payee_alias_directive(const string& payee, string alias)
|
||||||
|
{
|
||||||
|
trim(alias);
|
||||||
|
context.journal.payee_mappings
|
||||||
|
.push_back(payee_mapping_t(mask_t(alias), payee));
|
||||||
|
}
|
||||||
|
|
||||||
void instance_t::commodity_directive(char * line)
|
void instance_t::commodity_directive(char * line)
|
||||||
{
|
{
|
||||||
char * p = skip_ws(line);
|
char * p = skip_ws(line);
|
||||||
|
|
@ -933,53 +1002,65 @@ void instance_t::commodity_directive(char * line)
|
||||||
commodity_t::parse_symbol(p, symbol);
|
commodity_t::parse_symbol(p, symbol);
|
||||||
|
|
||||||
if (commodity_t * commodity =
|
if (commodity_t * commodity =
|
||||||
commodity_pool_t::current_pool->find_or_create(symbol))
|
commodity_pool_t::current_pool->find_or_create(symbol)) {
|
||||||
context.journal.register_commodity(*commodity, 0,
|
context.journal.register_commodity(*commodity, 0,
|
||||||
file_context(pathname, linenum));
|
file_context(pathname, linenum));
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void instance_t::commodity_alias_directive(char * line)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void instance_t::commodity_nomarket_directive(char * line)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void instance_t::account_mapping_directive(char * line)
|
|
||||||
{
|
|
||||||
char * account_name = skip_ws(line);
|
|
||||||
char * payee_regex = next_element(account_name, true);
|
|
||||||
|
|
||||||
if (payee_regex)
|
|
||||||
context.journal.account_mappings.push_back
|
|
||||||
(account_mapping_t(mask_t(payee_regex),
|
|
||||||
context.top_account()->find_account(account_name)));
|
|
||||||
|
|
||||||
while (peek_whitespace_line()) {
|
while (peek_whitespace_line()) {
|
||||||
#if defined(NO_ASSERTS)
|
|
||||||
read_line(line);
|
read_line(line);
|
||||||
#else
|
char * q = skip_ws(line);
|
||||||
std::streamsize len = read_line(line);
|
if (! *q)
|
||||||
assert(len > 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
payee_regex = skip_ws(line);
|
|
||||||
if (! *payee_regex)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
context.journal.account_mappings.push_back
|
char * b = next_element(q);
|
||||||
(account_mapping_t(mask_t(payee_regex),
|
string keyword(q);
|
||||||
context.top_account()->find_account(account_name)));
|
if (keyword == "alias")
|
||||||
|
commodity_alias_directive(*commodity, b);
|
||||||
|
else if (keyword == "format")
|
||||||
|
commodity_format_directive(*commodity, b);
|
||||||
|
else if (keyword == "nomarket")
|
||||||
|
commodity_nomarket_directive(*commodity);
|
||||||
|
else if (keyword == "default")
|
||||||
|
commodity_default_directive(*commodity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
void instance_t::define_directive(char * line)
|
void instance_t::commodity_alias_directive(commodity_t&, string)
|
||||||
{
|
{
|
||||||
expr_t def(skip_ws(line));
|
#if 0
|
||||||
def.compile(context.scope); // causes definitions to be established
|
trim(alias);
|
||||||
|
std::pair<commodity_pool_t::commodities_map::iterator, bool> result
|
||||||
|
= commodity_pool_t::current_pool->commodities.insert
|
||||||
|
(commodity_pool_t::commodities_map::value_type(alias, &comm));
|
||||||
|
if (! result.second)
|
||||||
|
throw_(parse_error,
|
||||||
|
_("Cannot use existing commodity name as an alias: %1") << alias);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::commodity_format_directive(commodity_t&, string format)
|
||||||
|
{
|
||||||
|
trim(format);
|
||||||
|
amount_t amt;
|
||||||
|
amt.parse(format);
|
||||||
|
VERIFY(amt.valid());
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::commodity_nomarket_directive(commodity_t& comm)
|
||||||
|
{
|
||||||
|
comm.add_flags(COMMODITY_NOMARKET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::commodity_default_directive(commodity_t& comm)
|
||||||
|
{
|
||||||
|
commodity_pool_t::current_pool->default_commodity = &comm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void instance_t::eval_directive(char * line)
|
||||||
|
{
|
||||||
|
expr_t expr(line);
|
||||||
|
expr.calc(context.scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::assert_directive(char * line)
|
void instance_t::assert_directive(char * line)
|
||||||
|
|
@ -1008,12 +1089,6 @@ void instance_t::comment_directive(char * line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::expr_directive(char * line)
|
|
||||||
{
|
|
||||||
expr_t expr(line);
|
|
||||||
expr.calc(context.scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool instance_t::general_directive(char * line)
|
bool instance_t::general_directive(char * line)
|
||||||
{
|
{
|
||||||
char buf[8192];
|
char buf[8192];
|
||||||
|
|
@ -1032,6 +1107,10 @@ bool instance_t::general_directive(char * line)
|
||||||
account_directive(arg);
|
account_directive(arg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (std::strcmp(p, "alias") == 0) {
|
||||||
|
alias_directive(arg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else if (std::strcmp(p, "apply") == 0) {
|
else if (std::strcmp(p, "apply") == 0) {
|
||||||
apply_directive(arg);
|
apply_directive(arg);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1066,7 +1145,7 @@ bool instance_t::general_directive(char * line)
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) {
|
if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) {
|
||||||
define_directive(arg);
|
eval_directive(arg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1076,8 +1155,8 @@ bool instance_t::general_directive(char * line)
|
||||||
end_apply_directive(arg);
|
end_apply_directive(arg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (std::strcmp(p, "expr") == 0) {
|
else if (std::strcmp(p, "expr") == 0 || std::strcmp(p, "eval") == 0) {
|
||||||
expr_directive(arg);
|
eval_directive(arg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1189,7 +1268,7 @@ post_t * instance_t::parse_post(char * line,
|
||||||
p++; e--;
|
p++; e--;
|
||||||
}
|
}
|
||||||
|
|
||||||
string name(p, static_cast<std::string::size_type>(e - p));
|
string name(p, static_cast<string::size_type>(e - p));
|
||||||
DEBUG("textual.parse", "line " << linenum << ": "
|
DEBUG("textual.parse", "line " << linenum << ": "
|
||||||
<< "Parsed account name " << name);
|
<< "Parsed account name " << name);
|
||||||
|
|
||||||
|
|
@ -1429,8 +1508,8 @@ post_t * instance_t::parse_post(char * line,
|
||||||
}
|
}
|
||||||
catch (const std::exception&) {
|
catch (const std::exception&) {
|
||||||
add_error_context(_("While parsing posting:"));
|
add_error_context(_("While parsing posting:"));
|
||||||
add_error_context(line_context(buf, static_cast<std::string::size_type>(beg),
|
add_error_context(line_context(buf, static_cast<string::size_type>(beg),
|
||||||
static_cast<std::string::size_type>(len)));
|
static_cast<string::size_type>(len)));
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
test/baseline/dir-account.test
Normal file
25
test/baseline/dir-account.test
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
account Assets:Cash
|
||||||
|
assert abs(amount) <= 20
|
||||||
|
check commodity == '$'
|
||||||
|
|
||||||
|
account Expenses:Food
|
||||||
|
alias food
|
||||||
|
payee KFC
|
||||||
|
|
||||||
|
commodity $
|
||||||
|
format $1,000.00
|
||||||
|
|
||||||
|
2012-02-27 KFC
|
||||||
|
Expenses:Unknown $20.00
|
||||||
|
Assets:Cash
|
||||||
|
|
||||||
|
2012-02-28 KFC
|
||||||
|
food $20.00
|
||||||
|
Assets:Cash
|
||||||
|
|
||||||
|
test reg --strict
|
||||||
|
12-Feb-27 KFC Expenses:Food $20.00 $20.00
|
||||||
|
Assets:Cash $-20.00 0
|
||||||
|
12-Feb-28 KFC Expenses:Food $20.00 $20.00
|
||||||
|
Assets:Cash $-20.00 0
|
||||||
|
end test
|
||||||
Loading…
Add table
Reference in a new issue