I have walked further along the road less compiled by...
This commit is contained in:
parent
52fc9f2e44
commit
20e35aa6f5
8 changed files with 131 additions and 153 deletions
12
journal.h
12
journal.h
|
|
@ -73,17 +73,17 @@ class transaction_t : public supports_flags<>
|
||||||
mutable void * data;
|
mutable void * data;
|
||||||
static bool use_effective_date;
|
static bool use_effective_date;
|
||||||
|
|
||||||
explicit transaction_t(account_t * _account = NULL)
|
transaction_t(account_t * _account = NULL)
|
||||||
: supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
|
: supports_flags<>(TRANSACTION_NORMAL), entry(NULL),
|
||||||
state(UNCLEARED), account(_account),
|
state(UNCLEARED), account(_account),
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
||||||
{
|
{
|
||||||
TRACE_CTOR(transaction_t, "account_t *");
|
TRACE_CTOR(transaction_t, "account_t *");
|
||||||
}
|
}
|
||||||
explicit transaction_t(account_t * _account,
|
transaction_t(account_t * _account,
|
||||||
const amount_t& _amount,
|
const amount_t& _amount,
|
||||||
unsigned int _flags = TRANSACTION_NORMAL,
|
unsigned int _flags = TRANSACTION_NORMAL,
|
||||||
const optional<string> _note = none)
|
const optional<string> _note = none)
|
||||||
: supports_flags<>(_flags), entry(NULL), state(UNCLEARED),
|
: supports_flags<>(_flags), entry(NULL), state(UNCLEARED),
|
||||||
account(_account), amount(_amount), note(_note),
|
account(_account), amount(_amount), note(_note),
|
||||||
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
beg_pos(0), beg_line(0), end_pos(0), end_line(0), data(NULL)
|
||||||
|
|
@ -91,7 +91,7 @@ class transaction_t : public supports_flags<>
|
||||||
TRACE_CTOR(transaction_t,
|
TRACE_CTOR(transaction_t,
|
||||||
"account_t *, const amount_t&, unsigned int, const string&");
|
"account_t *, const amount_t&, unsigned int, const string&");
|
||||||
}
|
}
|
||||||
explicit transaction_t(const transaction_t& xact)
|
transaction_t(const transaction_t& xact)
|
||||||
: supports_flags<>(xact),
|
: supports_flags<>(xact),
|
||||||
entry(xact.entry),
|
entry(xact.entry),
|
||||||
state(xact.state),
|
state(xact.state),
|
||||||
|
|
|
||||||
4
main.cc
4
main.cc
|
|
@ -36,8 +36,6 @@
|
||||||
//#endif
|
//#endif
|
||||||
//#include "qif.h"
|
//#include "qif.h"
|
||||||
//#include "ofx.h"
|
//#include "ofx.h"
|
||||||
#include "jbuilder.h"
|
|
||||||
#include "compile.h"
|
|
||||||
|
|
||||||
#include <ledger.h>
|
#include <ledger.h>
|
||||||
|
|
||||||
|
|
@ -109,7 +107,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
|
||||||
|
|
||||||
string verb = *arg++;
|
string verb = *arg++;
|
||||||
|
|
||||||
xml::xpath_t::function_t command;
|
expr::function_t command;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (verb == "register" || verb == "reg" || verb == "r")
|
if (verb == "register" || verb == "reg" || verb == "r")
|
||||||
|
|
|
||||||
18
session.cc
18
session.cc
|
|
@ -109,7 +109,9 @@ session_t::session_t()
|
||||||
|
|
||||||
now(now),
|
now(now),
|
||||||
|
|
||||||
|
#if 0
|
||||||
elision_style(ABBREVIATE),
|
elision_style(ABBREVIATE),
|
||||||
|
#endif
|
||||||
abbrev_length(2),
|
abbrev_length(2),
|
||||||
|
|
||||||
ansi_codes(false),
|
ansi_codes(false),
|
||||||
|
|
@ -118,6 +120,8 @@ session_t::session_t()
|
||||||
TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
|
TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
std::size_t session_t::read_journal(std::istream& in,
|
std::size_t session_t::read_journal(std::istream& in,
|
||||||
const path& pathname,
|
const path& pathname,
|
||||||
xml::builder_t& builder)
|
xml::builder_t& builder)
|
||||||
|
|
@ -223,6 +227,8 @@ std::size_t session_t::read_data(xml::builder_t& builder,
|
||||||
return entry_count;
|
return entry_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
optional<value_t>
|
optional<value_t>
|
||||||
session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
|
session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
|
||||||
|
|
@ -256,7 +262,7 @@ session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
|
expr::ptr_op_t session_t::lookup(const string& name)
|
||||||
{
|
{
|
||||||
const char * p = name.c_str();
|
const char * p = name.c_str();
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
|
|
@ -291,7 +297,7 @@ xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return xml::xpath_t::symbol_scope_t::lookup(name);
|
return expr::symbol_scope_t::lookup(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// jww (2007-04-26): All of Ledger should be accessed through a
|
// jww (2007-04-26): All of Ledger should be accessed through a
|
||||||
|
|
@ -300,12 +306,16 @@ static void initialize()
|
||||||
{
|
{
|
||||||
amount_t::initialize();
|
amount_t::initialize();
|
||||||
value_t::initialize();
|
value_t::initialize();
|
||||||
xml::xpath_t::initialize();
|
#if 0
|
||||||
|
expr::initialize();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void shutdown()
|
static void shutdown()
|
||||||
{
|
{
|
||||||
xml::xpath_t::shutdown();
|
#if 0
|
||||||
|
expr::shutdown();
|
||||||
|
#endif
|
||||||
value_t::shutdown();
|
value_t::shutdown();
|
||||||
amount_t::shutdown();
|
amount_t::shutdown();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
28
textual.cc
28
textual.cc
|
|
@ -45,8 +45,8 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount,
|
||||||
transaction_t * xact,
|
transaction_t * xact,
|
||||||
unsigned short flags = 0)
|
unsigned short flags = 0)
|
||||||
{
|
{
|
||||||
value_expr expr(parse_value_expr(in, NULL, flags | PARSE_VALEXPR_RELAXED |
|
value_expr expr(expr::parse_value_expr(in, NULL, flags | PARSE_VALEXPR_RELAXED |
|
||||||
PARSE_VALEXPR_PARTIAL)->acquire());
|
PARSE_VALEXPR_PARTIAL));
|
||||||
|
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Parsed an amount expression");
|
"Parsed an amount expression");
|
||||||
|
|
@ -60,11 +60,11 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (! compute_amount(expr, amount, xact))
|
if (! expr::compute_amount(expr, amount, xact))
|
||||||
throw new parse_error("Amount expression failed to compute");
|
throw new parse_error("Amount expression failed to compute");
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (expr->kind == expr::node_t::CONSTANT) {
|
if (expr->kind == expr::node_t::VALUE) {
|
||||||
expr = NULL;
|
expr = NULL;
|
||||||
} else {
|
} else {
|
||||||
DEBUG_IF("ledger.textual.parse") {
|
DEBUG_IF("ledger.textual.parse") {
|
||||||
|
|
@ -73,7 +73,7 @@ static value_expr parse_amount_expr(std::istream& in, amount_t& amount,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
expr = NULL;
|
expr = value_expr();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
|
|
@ -134,11 +134,11 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
||||||
char * e = &line[account_end];
|
char * e = &line[account_end];
|
||||||
if ((*b == '[' && *(e - 1) == ']') ||
|
if ((*b == '[' && *(e - 1) == ']') ||
|
||||||
(*b == '(' && *(e - 1) == ')')) {
|
(*b == '(' && *(e - 1) == ')')) {
|
||||||
xact->flags |= TRANSACTION_VIRTUAL;
|
xact->add_flags(TRANSACTION_VIRTUAL);
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Parsed a virtual account name");
|
"Parsed a virtual account name");
|
||||||
if (*b == '[') {
|
if (*b == '[') {
|
||||||
xact->flags |= TRANSACTION_BALANCE;
|
xact->add_flags(TRANSACTION_BALANCE);
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Parsed a balanced virtual account name");
|
"Parsed a balanced virtual account name");
|
||||||
}
|
}
|
||||||
|
|
@ -270,10 +270,10 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
||||||
p = peek_next_nonws(in);
|
p = peek_next_nonws(in);
|
||||||
xact->note = &line[in.tellg()];
|
xact->note = &line[in.tellg()];
|
||||||
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Parsed a note '" << xact->note << "'");
|
"Parsed a note '" << *xact->note << "'");
|
||||||
|
|
||||||
if (char * b = std::strchr(xact->note.c_str(), '['))
|
if (char * b = std::strchr(xact->note->c_str(), '['))
|
||||||
if (char * e = std::strchr(xact->note.c_str(), ']')) {
|
if (char * e = std::strchr(xact->note->c_str(), ']')) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
std::strncpy(buf, b + 1, e - b - 1);
|
std::strncpy(buf, b + 1, e - b - 1);
|
||||||
buf[e - b - 1] = '\0';
|
buf[e - b - 1] = '\0';
|
||||||
|
|
@ -835,12 +835,12 @@ unsigned int textual_parser_t::parse(std::istream& in,
|
||||||
// parser to resolve alias references.
|
// parser to resolve alias references.
|
||||||
account_t * acct = account_stack.front()->find_account(e);
|
account_t * acct = account_stack.front()->find_account(e);
|
||||||
std::pair<accounts_map::iterator, bool> result
|
std::pair<accounts_map::iterator, bool> result
|
||||||
= account_aliases.insert(accounts_pair(b, acct));
|
= account_aliases.insert(accounts_map::pair_type(b, acct));
|
||||||
assert(result.second);
|
assert(result.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (word == "def") {
|
else if (word == "def") {
|
||||||
if (! global_scope.get())
|
if (! expr::global_scope.get())
|
||||||
init_value_expr();
|
init_value_expr();
|
||||||
parse_value_definition(p);
|
parse_value_definition(p);
|
||||||
}
|
}
|
||||||
|
|
@ -980,7 +980,7 @@ void write_textual_journal(journal_t& journal, path pathname,
|
||||||
base = *el++;
|
base = *el++;
|
||||||
}
|
}
|
||||||
else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) {
|
else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) {
|
||||||
out << "= " << (*al)->predicate_string << '\n';
|
out << "= " << (*al)->predicate.predicate.expr << '\n';
|
||||||
base = *al++;
|
base = *al++;
|
||||||
}
|
}
|
||||||
else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) {
|
else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) {
|
||||||
|
|
@ -993,7 +993,7 @@ void write_textual_journal(journal_t& journal, path pathname,
|
||||||
for (transactions_list::iterator x = base->transactions.begin();
|
for (transactions_list::iterator x = base->transactions.begin();
|
||||||
x != base->transactions.end();
|
x != base->transactions.end();
|
||||||
x++)
|
x++)
|
||||||
if (! ((*x)->flags & TRANSACTION_AUTO)) {
|
if (! (*x)->has_flags(TRANSACTION_AUTO)) {
|
||||||
transaction_xdata(**x).dflags |= TRANSACTION_TO_DISPLAY;
|
transaction_xdata(**x).dflags |= TRANSACTION_TO_DISPLAY;
|
||||||
formatter(**x);
|
formatter(**x);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
186
valexpr.cc
186
valexpr.cc
|
|
@ -7,6 +7,8 @@ namespace ledger {
|
||||||
value_expr amount_expr;
|
value_expr amount_expr;
|
||||||
value_expr total_expr;
|
value_expr total_expr;
|
||||||
|
|
||||||
|
namespace expr {
|
||||||
|
|
||||||
std::auto_ptr<scope_t> global_scope;
|
std::auto_ptr<scope_t> global_scope;
|
||||||
datetime_t terminus;
|
datetime_t terminus;
|
||||||
|
|
||||||
|
|
@ -31,7 +33,7 @@ bool compute_amount(ptr_op_t expr, amount_t& amt,
|
||||||
err->context.push_back(new valexpr_context(expr));
|
err->context.push_back(new valexpr_context(expr));
|
||||||
error_context * last = err->context.back();
|
error_context * last = err->context.back();
|
||||||
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
|
if (valexpr_context * ctxt = dynamic_cast<valexpr_context *>(last)) {
|
||||||
ctxt->expr = expr->acquire();
|
ctxt->expr = expr;
|
||||||
ctxt->desc = "While computing amount expression:";
|
ctxt->desc = "While computing amount expression:";
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
|
|
@ -39,39 +41,6 @@ bool compute_amount(ptr_op_t expr, amount_t& amt,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
op_t::~op_t()
|
|
||||||
{
|
|
||||||
DEBUG("ledger.memory.dtors", "dtor op_t " << this);
|
|
||||||
|
|
||||||
DEBUG("ledger.valexpr.memory", "Destroying " << this);
|
|
||||||
assert(refc == 0);
|
|
||||||
|
|
||||||
if (left)
|
|
||||||
left->release();
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case F_CODE_MASK:
|
|
||||||
case F_PAYEE_MASK:
|
|
||||||
case F_NOTE_MASK:
|
|
||||||
case F_ACCOUNT_MASK:
|
|
||||||
case F_SHORT_ACCOUNT_MASK:
|
|
||||||
case F_COMMODITY_MASK:
|
|
||||||
assert(mask);
|
|
||||||
delete mask;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CONSTANT:
|
|
||||||
assert(value);
|
|
||||||
delete value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (kind > TERMINALS && right)
|
|
||||||
right->release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int count_leaves(ptr_op_t expr)
|
int count_leaves(ptr_op_t expr)
|
||||||
{
|
{
|
||||||
|
|
@ -79,8 +48,8 @@ namespace {
|
||||||
if (expr->kind != op_t::O_COM) {
|
if (expr->kind != op_t::O_COM) {
|
||||||
count = 1;
|
count = 1;
|
||||||
} else {
|
} else {
|
||||||
count += count_leaves(expr->left);
|
count += count_leaves(expr->left());
|
||||||
count += count_leaves(expr->right);
|
count += count_leaves(expr->right());
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
@ -97,19 +66,19 @@ namespace {
|
||||||
if (expr->kind < op_t::TERMINALS) {
|
if (expr->kind < op_t::TERMINALS) {
|
||||||
temp.reset(expr);
|
temp.reset(expr);
|
||||||
} else {
|
} else {
|
||||||
temp.reset(new op_t(op_t::CONSTANT));
|
temp.reset(new op_t(op_t::VALUE));
|
||||||
temp->value = new value_t;
|
temp->set_value(value_t());
|
||||||
expr->compute(*(temp->value), details, context);
|
expr->compute(temp->as_value(), details, context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
temp.reset(new op_t(op_t::O_COM));
|
temp.reset(new op_t(op_t::O_COM));
|
||||||
temp->set_left(reduce_leaves(expr->left, details, context));
|
temp->set_left(reduce_leaves(expr->left(), details, context));
|
||||||
temp->set_right(reduce_leaves(expr->right, details, context));
|
temp->set_right(reduce_leaves(expr->right(), details, context));
|
||||||
}
|
}
|
||||||
return temp.release();
|
return temp.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr_op_t find_leaf(ptr_op_t context, int goal, int& found)
|
ptr_op_t find_leaf(ptr_op_t context, int goal, long& found)
|
||||||
{
|
{
|
||||||
if (! context)
|
if (! context)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -118,10 +87,10 @@ namespace {
|
||||||
if (goal == found++)
|
if (goal == found++)
|
||||||
return context;
|
return context;
|
||||||
} else {
|
} else {
|
||||||
ptr_op_t expr = find_leaf(context->left, goal, found);
|
ptr_op_t expr = find_leaf(context->left(), goal, found);
|
||||||
if (expr)
|
if (expr)
|
||||||
return expr;
|
return expr;
|
||||||
expr = find_leaf(context->right, goal, found);
|
expr = find_leaf(context->right(), goal, found);
|
||||||
if (expr)
|
if (expr)
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -130,16 +99,15 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
void op_t::compute(value_t& result, const details_t& details,
|
void op_t::compute(value_t& result, const details_t& details,
|
||||||
ptr_op_t context) const
|
ptr_op_t context) const
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case ARG_INDEX:
|
case ARG_INDEX:
|
||||||
throw new compute_error("Cannot directly compute an arg_index");
|
throw new compute_error("Cannot directly compute an arg_index");
|
||||||
|
|
||||||
case CONSTANT:
|
case VALUE:
|
||||||
assert(value);
|
result = as_value();
|
||||||
result = *value;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_NOW:
|
case F_NOW:
|
||||||
|
|
@ -303,14 +271,14 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
|
|
||||||
case REAL:
|
case REAL:
|
||||||
if (details.xact)
|
if (details.xact)
|
||||||
result = ! (details.xact->flags & TRANSACTION_VIRTUAL);
|
result = ! (details.xact->has_flags(TRANSACTION_VIRTUAL));
|
||||||
else
|
else
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTUAL:
|
case ACTUAL:
|
||||||
if (details.xact)
|
if (details.xact)
|
||||||
result = ! (details.xact->flags & TRANSACTION_AUTO);
|
result = ! (details.xact->has_flags(TRANSACTION_AUTO));
|
||||||
else
|
else
|
||||||
result = true;
|
result = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -341,7 +309,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_PRICE: {
|
case F_PRICE: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
result = result.value();
|
result = result.value();
|
||||||
|
|
@ -349,7 +317,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_DATE: {
|
case F_DATE: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
result = result.as_datetime_lval();
|
result = result.as_datetime_lval();
|
||||||
|
|
@ -357,7 +325,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_DATECMP: {
|
case F_DATECMP: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
result = result.as_datetime_lval();
|
result = result.as_datetime_lval();
|
||||||
|
|
@ -382,7 +350,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
case F_YEAR:
|
case F_YEAR:
|
||||||
case F_MONTH:
|
case F_MONTH:
|
||||||
case F_DAY: {
|
case F_DAY: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
|
|
||||||
|
|
@ -408,7 +376,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_ARITH_MEAN: {
|
case F_ARITH_MEAN: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
if (details.xact && transaction_has_xdata(*details.xact)) {
|
if (details.xact && transaction_has_xdata(*details.xact)) {
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
|
|
@ -427,11 +395,11 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
|
|
||||||
case F_PARENT:
|
case F_PARENT:
|
||||||
if (details.account && details.account->parent)
|
if (details.account && details.account->parent)
|
||||||
left->compute(result, details_t(*details.account->parent), context);
|
left()->compute(result, details_t(*details.account->parent), context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_ABS: {
|
case F_ABS: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
result.abs();
|
result.abs();
|
||||||
|
|
@ -439,7 +407,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_ROUND: {
|
case F_ROUND: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
result.round();
|
result.round();
|
||||||
|
|
@ -447,7 +415,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_COMMODITY: {
|
case F_COMMODITY: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
if (! result.is_type(value_t::AMOUNT))
|
if (! result.is_type(value_t::AMOUNT))
|
||||||
|
|
@ -460,7 +428,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_SET_COMMODITY: {
|
case F_SET_COMMODITY: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
value_t temp;
|
value_t temp;
|
||||||
expr->compute(temp, details, context);
|
expr->compute(temp, details, context);
|
||||||
|
|
@ -481,7 +449,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_QUANTITY: {
|
case F_QUANTITY: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
|
|
||||||
|
|
@ -521,6 +489,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
case F_CODE_MASK:
|
case F_CODE_MASK:
|
||||||
assert(mask);
|
assert(mask);
|
||||||
if (details.entry)
|
if (details.entry)
|
||||||
|
|
@ -568,12 +537,12 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
else
|
else
|
||||||
result = false;
|
result = false;
|
||||||
break;
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case O_ARG: {
|
case O_ARG: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
assert(left);
|
assert(left()->kind == ARG_INDEX);
|
||||||
assert(left->kind == ARG_INDEX);
|
ptr_op_t expr = find_leaf(context, left()->as_long(), arg_index);
|
||||||
ptr_op_t expr = find_leaf(context, left->arg_index, arg_index);
|
|
||||||
if (expr)
|
if (expr)
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
else
|
else
|
||||||
|
|
@ -582,14 +551,14 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_COM:
|
case O_COM:
|
||||||
if (! left)
|
if (! left())
|
||||||
throw new compute_error("Comma operator missing left operand",
|
throw new compute_error("Comma operator missing left operand",
|
||||||
new valexpr_context(this));
|
new valexpr_context(this));
|
||||||
if (! right)
|
if (! right())
|
||||||
throw new compute_error("Comma operator missing right operand",
|
throw new compute_error("Comma operator missing right operand",
|
||||||
new valexpr_context(this));
|
new valexpr_context(this));
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
right->compute(result, details, context);
|
right()->compute(result, details, context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_DEF:
|
case O_DEF:
|
||||||
|
|
@ -597,18 +566,18 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_REF: {
|
case O_REF: {
|
||||||
assert(left);
|
assert(left());
|
||||||
if (right) {
|
if (right()) {
|
||||||
value_expr args(reduce_leaves(right, details, context));
|
value_expr args(reduce_leaves(right(), details, context));
|
||||||
left->compute(result, details, args.get());
|
left()->compute(result, details, args.get());
|
||||||
} else {
|
} else {
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case F_VALUE: {
|
case F_VALUE: {
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
ptr_op_t expr = find_leaf(context, 0, arg_index);
|
||||||
expr->compute(result, details, context);
|
expr->compute(result, details, context);
|
||||||
|
|
||||||
|
|
@ -625,7 +594,7 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_NOT:
|
case O_NOT:
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
if (result.strip_annotations())
|
if (result.strip_annotations())
|
||||||
result = false;
|
result = false;
|
||||||
else
|
else
|
||||||
|
|
@ -633,32 +602,32 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_QUES: {
|
case O_QUES: {
|
||||||
assert(left);
|
assert(left());
|
||||||
assert(right);
|
assert(right());
|
||||||
assert(right->kind == O_COL);
|
assert(right()->kind == O_COL);
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
if (result.strip_annotations())
|
if (result.strip_annotations())
|
||||||
right->left->compute(result, details, context);
|
right()->left()->compute(result, details, context);
|
||||||
else
|
else
|
||||||
right->right->compute(result, details, context);
|
right()->right()->compute(result, details, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_AND:
|
case O_AND:
|
||||||
assert(left);
|
assert(left());
|
||||||
assert(right);
|
assert(right());
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
result = result.strip_annotations();
|
result = result.strip_annotations();
|
||||||
if (result)
|
if (result)
|
||||||
right->compute(result, details, context);
|
right()->compute(result, details, context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_OR:
|
case O_OR:
|
||||||
assert(left);
|
assert(left());
|
||||||
assert(right);
|
assert(right());
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
if (! result.strip_annotations())
|
if (! result.strip_annotations())
|
||||||
right->compute(result, details, context);
|
right()->compute(result, details, context);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_NEQ:
|
case O_NEQ:
|
||||||
|
|
@ -667,11 +636,11 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
case O_LTE:
|
case O_LTE:
|
||||||
case O_GT:
|
case O_GT:
|
||||||
case O_GTE: {
|
case O_GTE: {
|
||||||
assert(left);
|
assert(left());
|
||||||
assert(right);
|
assert(right());
|
||||||
value_t temp;
|
value_t temp;
|
||||||
left->compute(temp, details, context);
|
left()->compute(temp, details, context);
|
||||||
right->compute(result, details, context);
|
right()->compute(result, details, context);
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case O_NEQ: result = temp != result; break;
|
case O_NEQ: result = temp != result; break;
|
||||||
case O_EQ: result = temp == result; break;
|
case O_EQ: result = temp == result; break;
|
||||||
|
|
@ -685,8 +654,8 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_NEG:
|
case O_NEG:
|
||||||
assert(left);
|
assert(left());
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
result.negate();
|
result.negate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -694,11 +663,11 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
case O_SUB:
|
case O_SUB:
|
||||||
case O_MUL:
|
case O_MUL:
|
||||||
case O_DIV: {
|
case O_DIV: {
|
||||||
assert(left);
|
assert(left());
|
||||||
assert(right);
|
assert(right());
|
||||||
value_t temp;
|
value_t temp;
|
||||||
right->compute(temp, details, context);
|
right()->compute(temp, details, context);
|
||||||
left->compute(result, details, context);
|
left()->compute(result, details, context);
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
case O_ADD: result += temp; break;
|
case O_ADD: result += temp; break;
|
||||||
case O_SUB: result -= temp; break;
|
case O_SUB: result -= temp; break;
|
||||||
|
|
@ -710,10 +679,10 @@ void op_t::compute(value_t& result, const details_t& details,
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_PERC: {
|
case O_PERC: {
|
||||||
assert(left);
|
assert(left());
|
||||||
result = "100.0%";
|
result = "100.0%";
|
||||||
value_t temp;
|
value_t temp;
|
||||||
left->compute(temp, details, context);
|
left()->compute(temp, details, context);
|
||||||
result *= temp;
|
result *= temp;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -799,8 +768,8 @@ ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.reset(new op_t(op_t::CONSTANT));
|
node.reset(new op_t(op_t::VALUE));
|
||||||
node->value = new value_t(temp);
|
node->set_value(temp);
|
||||||
goto parsed;
|
goto parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -810,8 +779,8 @@ ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
|
||||||
READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
|
READ_INTO(in, buf, 255, c, std::isdigit(c) || c == '.');
|
||||||
amount_t temp;
|
amount_t temp;
|
||||||
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
|
temp.parse(buf, AMOUNT_PARSE_NO_MIGRATE);
|
||||||
node.reset(new op_t(op_t::CONSTANT));
|
node.reset(new op_t(op_t::VALUE));
|
||||||
node->value = new value_t(temp);
|
node->set_value(temp);
|
||||||
goto parsed;
|
goto parsed;
|
||||||
}
|
}
|
||||||
else if (std::isalnum(c) || c == '_') {
|
else if (std::isalnum(c) || c == '_') {
|
||||||
|
|
@ -854,9 +823,9 @@ ptr_op_t parse_value_term(std::istream& in, scope_t * scope,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (definition) {
|
if (definition) {
|
||||||
std::auto_ptr<scope_t> params(new scope_t(scope));
|
std::auto_ptr<call_scope_t> params(new call_scope_t(scope));
|
||||||
|
|
||||||
int arg_index = 0;
|
long arg_index = 0;
|
||||||
if (have_args) {
|
if (have_args) {
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
|
||||||
|
|
@ -1996,4 +1965,5 @@ void dump_value_expr(std::ostream& out, const ptr_op_t node,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace expr
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
|
|
@ -602,8 +602,8 @@ class valexpr_context : public error_context {
|
||||||
ptr_op_t expr;
|
ptr_op_t expr;
|
||||||
ptr_op_t error_node;
|
ptr_op_t error_node;
|
||||||
|
|
||||||
valexpr_context(const ptr_op_t _expr,
|
valexpr_context(const ptr_op_t& _expr,
|
||||||
const string& desc = "") throw();
|
const string& desc = "") throw();
|
||||||
virtual ~valexpr_context() throw();
|
virtual ~valexpr_context() throw();
|
||||||
|
|
||||||
virtual void describe(std::ostream& out) const throw();
|
virtual void describe(std::ostream& out) const throw();
|
||||||
|
|
|
||||||
16
walk.cc
16
walk.cc
|
|
@ -113,7 +113,7 @@ void set_account_value::operator()(transaction_t& xact)
|
||||||
add_transaction_to(xact, xdata.value);
|
add_transaction_to(xact, xdata.value);
|
||||||
|
|
||||||
xdata.count++;
|
xdata.count++;
|
||||||
if (xact.flags & TRANSACTION_VIRTUAL)
|
if (xact.has_flags(TRANSACTION_VIRTUAL))
|
||||||
xdata.virtuals++;
|
xdata.virtuals++;
|
||||||
|
|
||||||
item_handler<transaction_t>::operator()(xact);
|
item_handler<transaction_t>::operator()(xact);
|
||||||
|
|
@ -190,7 +190,7 @@ void handle_value(const value_t& value,
|
||||||
temps.push_back(transaction_t(account));
|
temps.push_back(transaction_t(account));
|
||||||
transaction_t& xact(temps.back());
|
transaction_t& xact(temps.back());
|
||||||
xact.entry = entry;
|
xact.entry = entry;
|
||||||
xact.flags |= TRANSACTION_BULK_ALLOC;
|
xact.add_flags(TRANSACTION_BULK_ALLOC);
|
||||||
entry->add_transaction(&xact);
|
entry->add_transaction(&xact);
|
||||||
|
|
||||||
// If there are component transactions to associate with this
|
// If there are component transactions to associate with this
|
||||||
|
|
@ -205,9 +205,9 @@ void handle_value(const value_t& value,
|
||||||
|
|
||||||
if (account && account_has_xdata(*account))
|
if (account && account_has_xdata(*account))
|
||||||
if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_NON_VIRTUALS)) {
|
if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_NON_VIRTUALS)) {
|
||||||
xact.flags |= TRANSACTION_VIRTUAL;
|
xact.add_flags(TRANSACTION_VIRTUAL);
|
||||||
if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_UNB_VIRTUALS))
|
if (! (account_xdata_(*account).dflags & ACCOUNT_HAS_UNB_VIRTUALS))
|
||||||
xact.flags |= TRANSACTION_BALANCE;
|
xact.add_flags(TRANSACTION_BALANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
transaction_xdata_t& xdata(transaction_xdata(xact));
|
transaction_xdata_t& xdata(transaction_xdata(xact));
|
||||||
|
|
@ -295,7 +295,7 @@ void related_transactions::flush()
|
||||||
transaction_xdata_t& xdata = transaction_xdata(**j);
|
transaction_xdata_t& xdata = transaction_xdata(**j);
|
||||||
if (! (xdata.dflags & TRANSACTION_HANDLED) &&
|
if (! (xdata.dflags & TRANSACTION_HANDLED) &&
|
||||||
(! (xdata.dflags & TRANSACTION_RECEIVED) ?
|
(! (xdata.dflags & TRANSACTION_RECEIVED) ?
|
||||||
! ((*j)->flags & (TRANSACTION_AUTO | TRANSACTION_VIRTUAL)) :
|
! (*j)->has_flags(TRANSACTION_AUTO | TRANSACTION_VIRTUAL) :
|
||||||
also_matching)) {
|
also_matching)) {
|
||||||
xdata.dflags |= TRANSACTION_HANDLED;
|
xdata.dflags |= TRANSACTION_HANDLED;
|
||||||
item_handler<transaction_t>::operator()(**j);
|
item_handler<transaction_t>::operator()(**j);
|
||||||
|
|
@ -307,7 +307,7 @@ void related_transactions::flush()
|
||||||
// output auto or period entries.
|
// output auto or period entries.
|
||||||
transaction_xdata_t& xdata = transaction_xdata(**i);
|
transaction_xdata_t& xdata = transaction_xdata(**i);
|
||||||
if (! (xdata.dflags & TRANSACTION_HANDLED) &&
|
if (! (xdata.dflags & TRANSACTION_HANDLED) &&
|
||||||
! ((*i)->flags & TRANSACTION_AUTO)) {
|
! (*i)->has_flags(TRANSACTION_AUTO)) {
|
||||||
xdata.dflags |= TRANSACTION_HANDLED;
|
xdata.dflags |= TRANSACTION_HANDLED;
|
||||||
item_handler<transaction_t>::operator()(**i);
|
item_handler<transaction_t>::operator()(**i);
|
||||||
}
|
}
|
||||||
|
|
@ -436,9 +436,9 @@ void subtotal_transactions::operator()(transaction_t& xact)
|
||||||
// such, so that `handle_value' can show "(Account)" for accounts
|
// such, so that `handle_value' can show "(Account)" for accounts
|
||||||
// that contain only virtual transactions.
|
// that contain only virtual transactions.
|
||||||
|
|
||||||
if (! (xact.flags & TRANSACTION_VIRTUAL))
|
if (! xact.has_flags(TRANSACTION_VIRTUAL))
|
||||||
account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
|
account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_NON_VIRTUALS;
|
||||||
else if (! (xact.flags & TRANSACTION_BALANCE))
|
else if (! xact.has_flags(TRANSACTION_BALANCE))
|
||||||
account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
|
account_xdata(*xact_account(xact)).dflags |= ACCOUNT_HAS_UNB_VIRTUALS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
16
xml.cc
16
xml.cc
|
|
@ -97,10 +97,10 @@ static void endElement(void *userData, const char *name)
|
||||||
curr_entry->transactions.back()->state = transaction_t::PENDING;
|
curr_entry->transactions.back()->state = transaction_t::PENDING;
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "tr:virtual") == 0) {
|
else if (std::strcmp(name, "tr:virtual") == 0) {
|
||||||
curr_entry->transactions.back()->flags |= TRANSACTION_VIRTUAL;
|
curr_entry->transactions.back()->add_flags(TRANSACTION_VIRTUAL);
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "tr:generated") == 0) {
|
else if (std::strcmp(name, "tr:generated") == 0) {
|
||||||
curr_entry->transactions.back()->flags |= TRANSACTION_AUTO;
|
curr_entry->transactions.back()->add_flags(TRANSACTION_AUTO);
|
||||||
}
|
}
|
||||||
else if (std::strcmp(name, "symbol") == 0) {
|
else if (std::strcmp(name, "symbol") == 0) {
|
||||||
assert(! curr_comm);
|
assert(! curr_comm);
|
||||||
|
|
@ -370,9 +370,9 @@ void format_xml_entries::format_last_entry()
|
||||||
<< "</en:date_eff>\n";
|
<< "</en:date_eff>\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (! last_entry->code.empty()) {
|
if (last_entry->code) {
|
||||||
output_stream << " <en:code>";
|
output_stream << " <en:code>";
|
||||||
output_xml_string(output_stream, last_entry->code);
|
output_xml_string(output_stream, *last_entry->code);
|
||||||
output_stream << "</en:code>\n";
|
output_stream << "</en:code>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,9 +413,9 @@ void format_xml_entries::format_last_entry()
|
||||||
else if ((*i)->state == transaction_t::PENDING)
|
else if ((*i)->state == transaction_t::PENDING)
|
||||||
output_stream << " <tr:pending/>\n";
|
output_stream << " <tr:pending/>\n";
|
||||||
|
|
||||||
if ((*i)->flags & TRANSACTION_VIRTUAL)
|
if ((*i)->has_flags(TRANSACTION_VIRTUAL))
|
||||||
output_stream << " <tr:virtual/>\n";
|
output_stream << " <tr:virtual/>\n";
|
||||||
if ((*i)->flags & TRANSACTION_AUTO)
|
if ((*i)->has_flags(TRANSACTION_AUTO))
|
||||||
output_stream << " <tr:generated/>\n";
|
output_stream << " <tr:generated/>\n";
|
||||||
|
|
||||||
if ((*i)->account) {
|
if ((*i)->account) {
|
||||||
|
|
@ -444,9 +444,9 @@ void format_xml_entries::format_last_entry()
|
||||||
output_stream << " </tr:cost>\n";
|
output_stream << " </tr:cost>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! (*i)->note.empty()) {
|
if ((*i)->note) {
|
||||||
output_stream << " <tr:note>";
|
output_stream << " <tr:note>";
|
||||||
output_xml_string(output_stream, (*i)->note);
|
output_xml_string(output_stream, *(*i)->note);
|
||||||
output_stream << "</tr:note>\n";
|
output_stream << "</tr:note>\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue