Added support for metadata and tagging, and made regexs a first-class type.
This commit is contained in:
parent
7128fdb637
commit
fb5428ce85
16 changed files with 415 additions and 123 deletions
150
src/item.cc
150
src/item.cc
|
|
@ -37,6 +37,68 @@ namespace ledger {
|
||||||
|
|
||||||
bool item_t::use_effective_date = false;
|
bool item_t::use_effective_date = false;
|
||||||
|
|
||||||
|
bool item_t::has_tag(const string& tag) const
|
||||||
|
{
|
||||||
|
if (! metadata)
|
||||||
|
return false;
|
||||||
|
string_map::const_iterator i = metadata->find(tag);
|
||||||
|
return i != metadata->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<string> item_t::get_tag(const string& tag) const
|
||||||
|
{
|
||||||
|
if (metadata) {
|
||||||
|
string_map::const_iterator i = metadata->find(tag);
|
||||||
|
if (i != metadata->end())
|
||||||
|
return (*i).second;
|
||||||
|
}
|
||||||
|
return none;
|
||||||
|
}
|
||||||
|
|
||||||
|
void item_t::set_tag(const string& tag,
|
||||||
|
const optional<string>& value)
|
||||||
|
{
|
||||||
|
if (! metadata)
|
||||||
|
metadata = string_map();
|
||||||
|
|
||||||
|
DEBUG("item.meta", "Setting tag '" << tag << "' to value '"
|
||||||
|
<< (value ? *value : string("<none>")) << "'");
|
||||||
|
|
||||||
|
std::pair<string_map::iterator, bool> result
|
||||||
|
= metadata->insert(string_map::value_type(tag, value));
|
||||||
|
assert(result.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void item_t::parse_tags(const char * p)
|
||||||
|
{
|
||||||
|
if (! std::strchr(p, ':'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
scoped_array<char> buf(new char[std::strlen(p) + 1]);
|
||||||
|
|
||||||
|
std::strcpy(buf.get(), p);
|
||||||
|
|
||||||
|
string tag;
|
||||||
|
for (char * q = std::strtok(buf.get(), " \t");
|
||||||
|
q;
|
||||||
|
q = std::strtok(NULL, " \t")) {
|
||||||
|
const std::size_t len = std::strlen(q);
|
||||||
|
if (! tag.empty()) {
|
||||||
|
set_tag(tag, string(p + (q - buf.get())));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags
|
||||||
|
for (char * r = std::strtok(q + 1, ":");
|
||||||
|
r;
|
||||||
|
r = std::strtok(NULL, ":"))
|
||||||
|
set_tag(r);
|
||||||
|
}
|
||||||
|
else if (q[len - 1] == ':') { // a metadata setting
|
||||||
|
tag = string(q, len - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
value_t get_status(item_t& item) {
|
value_t get_status(item_t& item) {
|
||||||
return long(item.state());
|
return long(item.state());
|
||||||
|
|
@ -59,7 +121,51 @@ namespace {
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t get_note(item_t& item) {
|
value_t get_note(item_t& item) {
|
||||||
return string_value(item.note ? *item.note : empty_string);
|
return item.note ? string_value(*item.note) : value_t(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t has_tag(call_scope_t& args) {
|
||||||
|
item_t& item(find_scope<item_t>(args));
|
||||||
|
if (! item.metadata)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
IF_DEBUG("item.meta") {
|
||||||
|
foreach (const item_t::string_map::value_type& data, *item.metadata) {
|
||||||
|
*_log_stream << " Tag: " << data.first << "\n";
|
||||||
|
*_log_stream << "Value: ";
|
||||||
|
if (data.second)
|
||||||
|
*_log_stream << *data.second << "\n";
|
||||||
|
else
|
||||||
|
*_log_stream << "<none>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t& arg(args[0]);
|
||||||
|
|
||||||
|
if (arg.is_string()) {
|
||||||
|
if (args.size() == 1)
|
||||||
|
return item.has_tag(args[0].as_string());
|
||||||
|
else if (optional<string> tag = item.get_tag(args[0].as_string()))
|
||||||
|
return args[1] == string_value(*tag);
|
||||||
|
}
|
||||||
|
else if (arg.is_mask()) {
|
||||||
|
foreach (const item_t::string_map::value_type& data, *item.metadata) {
|
||||||
|
if (arg.as_mask().match(data.first)) {
|
||||||
|
if (args.size() == 1)
|
||||||
|
return true;
|
||||||
|
else if (data.second && args[1] == string_value(*data.second))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value_t get_tag(call_scope_t& args) {
|
||||||
|
item_t& item(find_scope<item_t>(args));
|
||||||
|
if (optional<string> value = item.get_tag(args[0].as_string()))
|
||||||
|
return string_value(*value);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_t get_beg_pos(item_t& item) {
|
value_t get_beg_pos(item_t& item) {
|
||||||
|
|
@ -84,12 +190,37 @@ namespace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_t get_comment(item_t& item)
|
||||||
|
{
|
||||||
|
if (! item.note) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << "\n ;";
|
||||||
|
bool need_separator = false;
|
||||||
|
for (const char * p = item.note->c_str(); *p; p++) {
|
||||||
|
if (*p == '\n')
|
||||||
|
need_separator = true;
|
||||||
|
else {
|
||||||
|
if (need_separator) {
|
||||||
|
buf << "\n ;";
|
||||||
|
need_separator = false;
|
||||||
|
}
|
||||||
|
buf << *p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return string_value(buf.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expr_t::ptr_op_t item_t::lookup(const string& name)
|
expr_t::ptr_op_t item_t::lookup(const string& name)
|
||||||
{
|
{
|
||||||
switch (name[0]) {
|
switch (name[0]) {
|
||||||
case 'c':
|
case 'c':
|
||||||
if (name == "cleared")
|
if (name == "cleared")
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_cleared>);
|
return WRAP_FUNCTOR(get_wrapper<&get_cleared>);
|
||||||
|
else if (name == "comment")
|
||||||
|
return WRAP_FUNCTOR(get_wrapper<&get_comment>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'd':
|
case 'd':
|
||||||
|
|
@ -97,6 +228,18 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_date>);
|
return WRAP_FUNCTOR(get_wrapper<&get_date>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
if (name == "has_tag")
|
||||||
|
return WRAP_FUNCTOR(ledger::has_tag);
|
||||||
|
else if (name == "has_meta")
|
||||||
|
return WRAP_FUNCTOR(ledger::has_tag);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
if (name == "meta")
|
||||||
|
return WRAP_FUNCTOR(ledger::get_tag);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
if (name == "note")
|
if (name == "note")
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_note>);
|
return WRAP_FUNCTOR(get_wrapper<&get_note>);
|
||||||
|
|
@ -112,6 +255,11 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_status>);
|
return WRAP_FUNCTOR(get_wrapper<&get_status>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
if (name == "tag")
|
||||||
|
return WRAP_FUNCTOR(ledger::get_tag);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
if (name == "uncleared")
|
if (name == "uncleared")
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_uncleared>);
|
return WRAP_FUNCTOR(get_wrapper<&get_uncleared>);
|
||||||
|
|
|
||||||
18
src/item.h
18
src/item.h
|
|
@ -72,6 +72,9 @@ public:
|
||||||
optional<date_t> _date_eff;
|
optional<date_t> _date_eff;
|
||||||
optional<string> note;
|
optional<string> note;
|
||||||
|
|
||||||
|
typedef std::map<string, optional<string> > string_map;
|
||||||
|
optional<string_map> metadata;
|
||||||
|
|
||||||
unsigned short src_idx;
|
unsigned short src_idx;
|
||||||
istream_pos_type full_beg_pos;
|
istream_pos_type full_beg_pos;
|
||||||
std::size_t full_beg_line;
|
std::size_t full_beg_line;
|
||||||
|
|
@ -122,6 +125,19 @@ public:
|
||||||
return ! (*this == entry);
|
return ! (*this == entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual bool has_tag(const string& tag) const;
|
||||||
|
virtual optional<string> get_tag(const string& tag) const;
|
||||||
|
virtual void set_tag(const string& tag,
|
||||||
|
const optional<string>& value = none);
|
||||||
|
virtual void parse_tags(const char * p);
|
||||||
|
|
||||||
|
virtual void append_note(const char * p) {
|
||||||
|
if (note)
|
||||||
|
*note += p;
|
||||||
|
else
|
||||||
|
note = p;
|
||||||
|
}
|
||||||
|
|
||||||
virtual optional<date_t> actual_date() const {
|
virtual optional<date_t> actual_date() const {
|
||||||
return _date;
|
return _date;
|
||||||
}
|
}
|
||||||
|
|
@ -147,6 +163,8 @@ public:
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
value_t get_comment(item_t& item);
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _ITEM_H
|
#endif // _ITEM_H
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,18 @@ public:
|
||||||
|
|
||||||
mask_t& operator=(const string& other);
|
mask_t& operator=(const string& other);
|
||||||
|
|
||||||
|
bool operator==(const mask_t& other) const {
|
||||||
|
return expr == other.expr;
|
||||||
|
}
|
||||||
|
|
||||||
bool match(const string& str) const {
|
bool match(const string& str) const {
|
||||||
return boost::regex_search(str, expr);
|
return boost::regex_search(str, expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool empty() const {
|
||||||
|
return expr.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void read(const char *& data);
|
void read(const char *& data);
|
||||||
void write(std::ostream& out) const;
|
void write(std::ostream& out) const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
97
src/op.cc
97
src/op.cc
|
|
@ -57,7 +57,9 @@ expr_t::ptr_op_t expr_t::op_t::compile(scope_t& scope)
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
ptr_op_t lhs(left()->compile(scope));
|
ptr_op_t lhs(left()->compile(scope));
|
||||||
ptr_op_t rhs(has_right() ? right()->compile(scope) : ptr_op_t());
|
ptr_op_t rhs(has_right() ? (kind == O_LOOKUP ?
|
||||||
|
right() : right()->compile(scope)) :
|
||||||
|
ptr_op_t());
|
||||||
|
|
||||||
if (lhs == left() && (! rhs || rhs == right()))
|
if (lhs == left() && (! rhs || rhs == right()))
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -102,11 +104,6 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
|
||||||
throw_(calc_error, "Unknown identifier '" << as_ident() << "'");
|
throw_(calc_error, "Unknown identifier '" << as_ident() << "'");
|
||||||
return left()->opcalc(scope);
|
return left()->opcalc(scope);
|
||||||
|
|
||||||
case MASK:
|
|
||||||
throw_(calc_error,
|
|
||||||
"Regexs can only be used in a match; did you mean: account =~ /"
|
|
||||||
<< as_mask() << '/');
|
|
||||||
|
|
||||||
case FUNCTION: {
|
case FUNCTION: {
|
||||||
// Evaluating a FUNCTION is the same as calling it directly; this happens
|
// Evaluating a FUNCTION is the same as calling it directly; this happens
|
||||||
// when certain functions-that-look-like-variables (such as "amount") are
|
// when certain functions-that-look-like-variables (such as "amount") are
|
||||||
|
|
@ -115,6 +112,27 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
|
||||||
return as_function()(call_args);
|
return as_function()(call_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case O_LOOKUP:
|
||||||
|
if (left()->kind == IDENT &&
|
||||||
|
left()->left() && left()->left()->kind == FUNCTION) {
|
||||||
|
call_scope_t call_args(scope);
|
||||||
|
if (value_t obj = left()->left()->as_function()(call_args)) {
|
||||||
|
if (obj.is_pointer()) {
|
||||||
|
scope_t& objscope(obj.as_ref_lval<scope_t>());
|
||||||
|
if (ptr_op_t member = objscope.lookup(right()->as_ident()))
|
||||||
|
return member->calc(objscope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (right()->kind != IDENT) {
|
||||||
|
throw_(calc_error,
|
||||||
|
"Right operand of . operator must be an identifier");
|
||||||
|
} else {
|
||||||
|
throw_(calc_error,
|
||||||
|
"Failed to lookup member '" << right()->as_ident() << "'");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case O_CALL: {
|
case O_CALL: {
|
||||||
call_scope_t call_args(scope);
|
call_scope_t call_args(scope);
|
||||||
|
|
||||||
|
|
@ -122,20 +140,19 @@ value_t expr_t::op_t::opcalc(scope_t& scope)
|
||||||
call_args.set_args(right()->opcalc(scope));
|
call_args.set_args(right()->opcalc(scope));
|
||||||
|
|
||||||
ptr_op_t func = left();
|
ptr_op_t func = left();
|
||||||
|
const string& name(func->as_ident());
|
||||||
|
|
||||||
assert(func->kind == IDENT);
|
|
||||||
func = func->left();
|
func = func->left();
|
||||||
|
|
||||||
if (! func || func->kind != FUNCTION)
|
if (! func || func->kind != FUNCTION)
|
||||||
throw_(calc_error, "Calling non-function");
|
throw_(calc_error, "Calling non-function '" << name << "'");
|
||||||
|
|
||||||
return func->as_function()(call_args);
|
return func->as_function()(call_args);
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_MATCH:
|
case O_MATCH:
|
||||||
if (! right()->is_mask())
|
if (! right()->is_value() || ! right()->as_value().is_mask())
|
||||||
throw_(calc_error, "Right-hand argument to match operator must be a regex");
|
throw_(calc_error, "Right-hand argument to match operator must be a regex");
|
||||||
return right()->as_mask().match(left()->opcalc(scope).to_string());
|
return right()->as_value().as_mask().match(left()->opcalc(scope).to_string());
|
||||||
|
|
||||||
case INDEX: {
|
case INDEX: {
|
||||||
const call_scope_t& args(downcast<const call_scope_t>(scope));
|
const call_scope_t& args(downcast<const call_scope_t>(scope));
|
||||||
|
|
@ -237,10 +254,6 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
|
||||||
out << as_ident();
|
out << as_ident();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MASK:
|
|
||||||
out << '/' << as_mask() << '/';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FUNCTION:
|
case FUNCTION:
|
||||||
out << "<FUNCTION>";
|
out << "<FUNCTION>";
|
||||||
break;
|
break;
|
||||||
|
|
@ -370,6 +383,14 @@ bool expr_t::op_t::print(std::ostream& out, const context_t& context) const
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case O_LOOKUP:
|
||||||
|
if (left() && left()->print(out, context))
|
||||||
|
found = true;
|
||||||
|
out << ".";
|
||||||
|
if (has_right() && right()->print(out, context))
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case O_CALL:
|
case O_CALL:
|
||||||
if (left() && left()->print(out, context))
|
if (left() && left()->print(out, context))
|
||||||
found = true;
|
found = true;
|
||||||
|
|
@ -425,10 +446,6 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
|
||||||
out << "IDENT: " << as_ident();
|
out << "IDENT: " << as_ident();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MASK:
|
|
||||||
out << "MASK: " << as_mask();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case INDEX:
|
case INDEX:
|
||||||
out << "INDEX: " << as_index();
|
out << "INDEX: " << as_index();
|
||||||
break;
|
break;
|
||||||
|
|
@ -437,27 +454,28 @@ void expr_t::op_t::dump(std::ostream& out, const int depth) const
|
||||||
out << "FUNCTION";
|
out << "FUNCTION";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_CALL: out << "O_CALL"; break;
|
case O_LOOKUP: out << "O_LOOKUP"; break;
|
||||||
case O_MATCH: out << "O_MATCH"; break;
|
case O_CALL: out << "O_CALL"; break;
|
||||||
|
case O_MATCH: out << "O_MATCH"; break;
|
||||||
|
|
||||||
case O_NOT: out << "O_NOT"; break;
|
case O_NOT: out << "O_NOT"; break;
|
||||||
case O_NEG: out << "O_NEG"; break;
|
case O_NEG: out << "O_NEG"; break;
|
||||||
|
|
||||||
case O_ADD: out << "O_ADD"; break;
|
case O_ADD: out << "O_ADD"; break;
|
||||||
case O_SUB: out << "O_SUB"; break;
|
case O_SUB: out << "O_SUB"; break;
|
||||||
case O_MUL: out << "O_MUL"; break;
|
case O_MUL: out << "O_MUL"; break;
|
||||||
case O_DIV: out << "O_DIV"; break;
|
case O_DIV: out << "O_DIV"; break;
|
||||||
|
|
||||||
case O_EQ: out << "O_EQ"; break;
|
case O_EQ: out << "O_EQ"; break;
|
||||||
case O_LT: out << "O_LT"; break;
|
case O_LT: out << "O_LT"; break;
|
||||||
case O_LTE: out << "O_LTE"; break;
|
case O_LTE: out << "O_LTE"; break;
|
||||||
case O_GT: out << "O_GT"; break;
|
case O_GT: out << "O_GT"; break;
|
||||||
case O_GTE: out << "O_GTE"; break;
|
case O_GTE: out << "O_GTE"; break;
|
||||||
|
|
||||||
case O_AND: out << "O_AND"; break;
|
case O_AND: out << "O_AND"; break;
|
||||||
case O_OR: out << "O_OR"; break;
|
case O_OR: out << "O_OR"; break;
|
||||||
|
|
||||||
case O_COMMA: out << "O_COMMA"; break;
|
case O_COMMA: out << "O_COMMA"; break;
|
||||||
|
|
||||||
case LAST:
|
case LAST:
|
||||||
default:
|
default:
|
||||||
|
|
@ -508,12 +526,6 @@ void expr_t::op_t::read(const char *& data)
|
||||||
set_ident(temp);
|
set_ident(temp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MASK: {
|
|
||||||
mask_t temp;
|
|
||||||
temp.read(data);
|
|
||||||
set_mask(temp);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INDEX: {
|
case INDEX: {
|
||||||
long temp;
|
long temp;
|
||||||
binary::read_long(data, temp);
|
binary::read_long(data, temp);
|
||||||
|
|
@ -550,9 +562,6 @@ void expr_t::op_t::write(std::ostream& out) const
|
||||||
case IDENT:
|
case IDENT:
|
||||||
binary::write_string(out, as_ident());
|
binary::write_string(out, as_ident());
|
||||||
break;
|
break;
|
||||||
case MASK:
|
|
||||||
as_mask().write(out);
|
|
||||||
break;
|
|
||||||
case INDEX:
|
case INDEX:
|
||||||
binary::write_long(out, as_index());
|
binary::write_long(out, as_index());
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
25
src/op.h
25
src/op.h
|
|
@ -47,7 +47,6 @@
|
||||||
#define _OP_H
|
#define _OP_H
|
||||||
|
|
||||||
#include "expr.h"
|
#include "expr.h"
|
||||||
#include "mask.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -71,7 +70,6 @@ private:
|
||||||
variant<std::size_t, // used by constant INDEX
|
variant<std::size_t, // used by constant INDEX
|
||||||
value_t, // used by constant VALUE
|
value_t, // used by constant VALUE
|
||||||
string, // used by constant IDENT
|
string, // used by constant IDENT
|
||||||
mask_t, // used by constant MASK
|
|
||||||
function_t, // used by terminal FUNCTION
|
function_t, // used by terminal FUNCTION
|
||||||
ptr_op_t> // used by all binary operators
|
ptr_op_t> // used by all binary operators
|
||||||
data;
|
data;
|
||||||
|
|
@ -81,7 +79,6 @@ public:
|
||||||
// Constants
|
// Constants
|
||||||
VALUE,
|
VALUE,
|
||||||
IDENT,
|
IDENT,
|
||||||
MASK,
|
|
||||||
INDEX,
|
INDEX,
|
||||||
|
|
||||||
CONSTANTS,
|
CONSTANTS,
|
||||||
|
|
@ -115,6 +112,7 @@ public:
|
||||||
|
|
||||||
O_COMMA,
|
O_COMMA,
|
||||||
|
|
||||||
|
O_LOOKUP,
|
||||||
O_CALL,
|
O_CALL,
|
||||||
O_MATCH,
|
O_MATCH,
|
||||||
|
|
||||||
|
|
@ -193,27 +191,6 @@ public:
|
||||||
data = val;
|
data = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_mask() const {
|
|
||||||
if (kind == MASK) {
|
|
||||||
assert(data.type() == typeid(mask_t));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mask_t& as_mask_lval() {
|
|
||||||
assert(is_mask());
|
|
||||||
return boost::get<mask_t>(data);
|
|
||||||
}
|
|
||||||
const mask_t& as_mask() const {
|
|
||||||
return const_cast<op_t *>(this)->as_mask_lval();
|
|
||||||
}
|
|
||||||
void set_mask(const mask_t& val) {
|
|
||||||
data = val;
|
|
||||||
}
|
|
||||||
void set_mask(const string& expr) {
|
|
||||||
data = mask_t(expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_function() const {
|
bool is_function() const {
|
||||||
return kind == FUNCTION;
|
return kind == FUNCTION;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,6 @@ expr_t::parser_t::parse_value_term(std::istream& in,
|
||||||
node->set_value(tok.value);
|
node->set_value(tok.value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case token_t::MASK:
|
|
||||||
node = new op_t(op_t::MASK);
|
|
||||||
node->set_mask(tok.value.as_string());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case token_t::IDENT: {
|
case token_t::IDENT: {
|
||||||
string ident = tok.value.as_string();
|
string ident = tok.value.as_string();
|
||||||
|
|
||||||
|
|
@ -88,6 +83,31 @@ expr_t::parser_t::parse_value_term(std::istream& in,
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_t::ptr_op_t
|
||||||
|
expr_t::parser_t::parse_dot_expr(std::istream& in,
|
||||||
|
const parse_flags_t& tflags) const
|
||||||
|
{
|
||||||
|
ptr_op_t node(parse_value_term(in, tflags));
|
||||||
|
|
||||||
|
if (node && ! tflags.has_flags(PARSE_SINGLE)) {
|
||||||
|
token_t& tok = next_token(in, tflags);
|
||||||
|
|
||||||
|
if (tok.kind == token_t::DOT) {
|
||||||
|
ptr_op_t prev(node);
|
||||||
|
node = new op_t(op_t::O_LOOKUP);
|
||||||
|
node->set_left(prev);
|
||||||
|
node->set_right(parse_dot_expr(in, tflags));
|
||||||
|
if (! node->right())
|
||||||
|
throw_(parse_error,
|
||||||
|
tok.symbol << " operator not followed by argument");
|
||||||
|
} else {
|
||||||
|
push_token(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
expr_t::ptr_op_t
|
expr_t::ptr_op_t
|
||||||
expr_t::parser_t::parse_unary_expr(std::istream& in,
|
expr_t::parser_t::parse_unary_expr(std::istream& in,
|
||||||
const parse_flags_t& tflags) const
|
const parse_flags_t& tflags) const
|
||||||
|
|
@ -98,7 +118,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
|
||||||
|
|
||||||
switch (tok.kind) {
|
switch (tok.kind) {
|
||||||
case token_t::EXCLAM: {
|
case token_t::EXCLAM: {
|
||||||
ptr_op_t term(parse_value_term(in, tflags));
|
ptr_op_t term(parse_dot_expr(in, tflags));
|
||||||
if (! term)
|
if (! term)
|
||||||
throw_(parse_error,
|
throw_(parse_error,
|
||||||
tok.symbol << " operator not followed by argument");
|
tok.symbol << " operator not followed by argument");
|
||||||
|
|
@ -115,7 +135,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
|
||||||
}
|
}
|
||||||
|
|
||||||
case token_t::MINUS: {
|
case token_t::MINUS: {
|
||||||
ptr_op_t term(parse_value_term(in, tflags));
|
ptr_op_t term(parse_dot_expr(in, tflags));
|
||||||
if (! term)
|
if (! term)
|
||||||
throw_(parse_error,
|
throw_(parse_error,
|
||||||
tok.symbol << " operator not followed by argument");
|
tok.symbol << " operator not followed by argument");
|
||||||
|
|
@ -133,7 +153,7 @@ expr_t::parser_t::parse_unary_expr(std::istream& in,
|
||||||
|
|
||||||
default:
|
default:
|
||||||
push_token(tok);
|
push_token(tok);
|
||||||
node = parse_value_term(in, tflags);
|
node = parse_dot_expr(in, tflags);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,8 @@ private:
|
||||||
|
|
||||||
ptr_op_t parse_value_term(std::istream& in,
|
ptr_op_t parse_value_term(std::istream& in,
|
||||||
const parse_flags_t& flags) const;
|
const parse_flags_t& flags) const;
|
||||||
|
ptr_op_t parse_dot_expr(std::istream& in,
|
||||||
|
const parse_flags_t& flags) const;
|
||||||
ptr_op_t parse_unary_expr(std::istream& in,
|
ptr_op_t parse_unary_expr(std::istream& in,
|
||||||
const parse_flags_t& flags) const;
|
const parse_flags_t& flags) const;
|
||||||
ptr_op_t parse_mul_expr(std::istream& in,
|
ptr_op_t parse_mul_expr(std::istream& in,
|
||||||
|
|
|
||||||
|
|
@ -67,27 +67,29 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
|
||||||
append_and = true;
|
append_and = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg == "desc" || arg == "DESC" ||
|
value_t::sequence_t::const_iterator next = begin;
|
||||||
arg == "payee" || arg == "PAYEE") {
|
if (++next != end) {
|
||||||
arg = string("@") + (*++begin).as_string();
|
if (arg == "desc" || arg == "DESC" ||
|
||||||
}
|
arg == "payee" || arg == "PAYEE") {
|
||||||
else if (arg == "note" || arg == "NOTE") {
|
arg = string("@") + (*++begin).as_string();
|
||||||
arg = string("&") + (*++begin).as_string();
|
}
|
||||||
}
|
else if (arg == "note" || arg == "NOTE") {
|
||||||
else if (arg == "tag" || arg == "TAG" ||
|
arg = string("&") + (*++begin).as_string();
|
||||||
arg == "meta" || arg == "META" ||
|
}
|
||||||
arg == "data" || arg == "DATA") {
|
else if (arg == "tag" || arg == "TAG" ||
|
||||||
arg = string("%") + (*++begin).as_string();
|
arg == "meta" || arg == "META" ||
|
||||||
}
|
arg == "data" || arg == "DATA") {
|
||||||
else if (arg == "expr" || arg == "EXPR") {
|
arg = string("%") + (*++begin).as_string();
|
||||||
arg = string("=") + (*++begin).as_string();
|
}
|
||||||
|
else if (arg == "expr" || arg == "EXPR") {
|
||||||
|
arg = string("=") + (*++begin).as_string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parse_argument) {
|
if (parse_argument) {
|
||||||
bool in_prefix = true;
|
bool in_prefix = true;
|
||||||
bool in_suffix = false;
|
bool in_suffix = false;
|
||||||
bool found_specifier = false;
|
bool found_specifier = false;
|
||||||
bool saw_tag_char = false;
|
|
||||||
bool no_final_slash = false;
|
bool no_final_slash = false;
|
||||||
|
|
||||||
only_parenthesis = true;
|
only_parenthesis = true;
|
||||||
|
|
@ -122,15 +124,14 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
|
||||||
bool found_metadata = false;
|
bool found_metadata = false;
|
||||||
for (const char *q = c; *q != '\0'; q++)
|
for (const char *q = c; *q != '\0'; q++)
|
||||||
if (*q == '=') {
|
if (*q == '=') {
|
||||||
expr << "(metadata(\""
|
expr << "has_tag(/"
|
||||||
<< string(c + 1, q - c - 1) << "\") =~ /";
|
<< string(c + 1, q - c - 1) << "/, /";
|
||||||
found_metadata = true;
|
found_metadata = true;
|
||||||
c = q;
|
c = q;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (! found_metadata) {
|
if (! found_metadata) {
|
||||||
expr << "(tag =~ /:";
|
expr << "has_tag(/";
|
||||||
saw_tag_char = true;
|
|
||||||
}
|
}
|
||||||
found_specifier = true;
|
found_specifier = true;
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
|
@ -151,9 +152,9 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
|
||||||
case ')':
|
case ')':
|
||||||
if (! in_suffix) {
|
if (! in_suffix) {
|
||||||
if (found_specifier) {
|
if (found_specifier) {
|
||||||
if (saw_tag_char)
|
if (! no_final_slash)
|
||||||
expr << ':';
|
expr << "/";
|
||||||
expr << "/)";
|
expr << ")";
|
||||||
}
|
}
|
||||||
in_suffix = true;
|
in_suffix = true;
|
||||||
}
|
}
|
||||||
|
|
@ -171,8 +172,6 @@ string args_to_predicate_expr(value_t::sequence_t::const_iterator begin,
|
||||||
|
|
||||||
if (! in_suffix) {
|
if (! in_suffix) {
|
||||||
if (found_specifier) {
|
if (found_specifier) {
|
||||||
if (saw_tag_char)
|
|
||||||
expr << ':';
|
|
||||||
if (! no_final_slash)
|
if (! no_final_slash)
|
||||||
expr << "/";
|
expr << "/";
|
||||||
expr << ")";
|
expr << ")";
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,8 @@ namespace {
|
||||||
(args_to_predicate_expr(args.value().as_sequence().begin(),
|
(args_to_predicate_expr(args.value().as_sequence().begin(),
|
||||||
args.value().as_sequence().end()));
|
args.value().as_sequence().end()));
|
||||||
|
|
||||||
|
DEBUG("report.predicate", "Predicate = " << report.predicate);
|
||||||
|
|
||||||
(report.*report_method)(handler_ptr(handler));
|
(report.*report_method)(handler_ptr(handler));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ session_t::session_t()
|
||||||
("%-.9D %-.35P %-.39A %22.108t %!22.132T\n%/"
|
("%-.9D %-.35P %-.39A %22.108t %!22.132T\n%/"
|
||||||
"%48|%-.38A %22.108t %!22.132T\n"),
|
"%48|%-.38A %22.108t %!22.132T\n"),
|
||||||
print_format
|
print_format
|
||||||
("%(date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)\n %-34(account) %12(amount)\n%/ %-34(account) %12(amount)%(note ? \" ; \" + note : \"\")\n%/\n"),
|
("%(date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)%(entry.comment | \"\")\n %-34(account) %12(amount)%(comment | \"\")\n%/ %-34(account) %12(amount)%(comment | \"\")\n%/\n"),
|
||||||
balance_format
|
balance_format
|
||||||
("%20(display_total) %(depth_spacer)%-(partial_account)\n"),
|
("%20(display_total) %(depth_spacer)%-(partial_account)\n"),
|
||||||
equity_format
|
equity_format
|
||||||
|
|
|
||||||
|
|
@ -1068,7 +1068,8 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
|
||||||
TRACE_START(entry_details, 1, "Time spent parsing entry details:");
|
TRACE_START(entry_details, 1, "Time spent parsing entry details:");
|
||||||
|
|
||||||
istream_pos_type end_pos;
|
istream_pos_type end_pos;
|
||||||
std::size_t beg_line = linenum;
|
std::size_t beg_line = linenum;
|
||||||
|
xact_t * last_xact = NULL;
|
||||||
|
|
||||||
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
|
while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) {
|
||||||
istream_pos_type beg_pos = in.tellg();
|
istream_pos_type beg_pos = in.tellg();
|
||||||
|
|
@ -1086,13 +1087,23 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
|
||||||
end_pos += len + 1;
|
end_pos += len + 1;
|
||||||
linenum++;
|
linenum++;
|
||||||
|
|
||||||
if (line[0] == ' ' || line[0] == '\t') {
|
const char * p = skip_ws(line);
|
||||||
char * p = skip_ws(line);
|
if (! *p)
|
||||||
if (! *p)
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xact_t * xact = parse_xact(line, master, curr.get())) {
|
if (*p == ';') {
|
||||||
|
// This is an entry note, and possibly a metadata info tag
|
||||||
|
if (last_xact) {
|
||||||
|
last_xact->append_note(p + 1);
|
||||||
|
last_xact->append_note("\n");
|
||||||
|
last_xact->parse_tags(p + 1);
|
||||||
|
} else {
|
||||||
|
curr->append_note(p + 1);
|
||||||
|
curr->append_note("\n");
|
||||||
|
curr->parse_tags(p + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (xact_t * xact = parse_xact(line, master, curr.get())) {
|
||||||
if ((state == item_t::CLEARED && xact->state() != item_t::CLEARED) ||
|
if ((state == item_t::CLEARED && xact->state() != item_t::CLEARED) ||
|
||||||
(state == item_t::PENDING && xact->state() == item_t::UNCLEARED))
|
(state == item_t::PENDING && xact->state() == item_t::UNCLEARED))
|
||||||
xact->set_state(state);
|
xact->set_state(state);
|
||||||
|
|
@ -1105,6 +1116,7 @@ entry_t * textual_parser_t::instance_t::parse_entry(std::istream& in,
|
||||||
pos = end_pos;
|
pos = end_pos;
|
||||||
|
|
||||||
curr->add_xact(xact);
|
curr->add_xact(xact);
|
||||||
|
last_xact = xact;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in.eof())
|
if (in.eof())
|
||||||
|
|
|
||||||
12
src/token.cc
12
src/token.cc
|
|
@ -110,8 +110,7 @@ void expr_t::token_t::parse_ident(std::istream& in)
|
||||||
length = 0;
|
length = 0;
|
||||||
|
|
||||||
char c, buf[256];
|
char c, buf[256];
|
||||||
READ_INTO_(in, buf, 255, c, length,
|
READ_INTO_(in, buf, 255, c, length, std::isalnum(c) || c == '_' || c == '-');
|
||||||
std::isalnum(c) || c == '_' || c == '.' || c == '-');
|
|
||||||
|
|
||||||
value.set_string(buf);
|
value.set_string(buf);
|
||||||
}
|
}
|
||||||
|
|
@ -280,8 +279,8 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
|
||||||
in.get(c);
|
in.get(c);
|
||||||
length++;
|
length++;
|
||||||
|
|
||||||
kind = MASK;
|
kind = VALUE;
|
||||||
value.set_string(buf);
|
value.set_mask(buf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -334,6 +333,11 @@ void expr_t::token_t::next(std::istream& in, const uint_least8_t pflags)
|
||||||
kind = GREATER;
|
kind = GREATER;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
in.get(c);
|
||||||
|
kind = DOT;
|
||||||
|
break;
|
||||||
|
|
||||||
case ',':
|
case ',':
|
||||||
in.get(c);
|
in.get(c);
|
||||||
kind = COMMA;
|
kind = COMMA;
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ struct expr_t::token_t : public noncopyable
|
||||||
QUERY, // ?
|
QUERY, // ?
|
||||||
COLON, // :
|
COLON, // :
|
||||||
|
|
||||||
|
DOT, // .
|
||||||
COMMA, // ,
|
COMMA, // ,
|
||||||
|
|
||||||
TOK_EOF,
|
TOK_EOF,
|
||||||
|
|
|
||||||
61
src/value.cc
61
src/value.cc
|
|
@ -76,6 +76,11 @@ value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs)
|
||||||
string(*reinterpret_cast<string *>(const_cast<char *>(rhs.data)));
|
string(*reinterpret_cast<string *>(const_cast<char *>(rhs.data)));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MASK:
|
||||||
|
new(reinterpret_cast<mask_t *>(data))
|
||||||
|
mask_t(*reinterpret_cast<mask_t *>(const_cast<char *>(rhs.data)));
|
||||||
|
break;
|
||||||
|
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
*reinterpret_cast<sequence_t **>(data) =
|
*reinterpret_cast<sequence_t **>(data) =
|
||||||
new sequence_t(**reinterpret_cast<sequence_t **>
|
new sequence_t(**reinterpret_cast<sequence_t **>
|
||||||
|
|
@ -107,6 +112,9 @@ void value_t::storage_t::destroy()
|
||||||
case STRING:
|
case STRING:
|
||||||
reinterpret_cast<string *>(data)->~string();
|
reinterpret_cast<string *>(data)->~string();
|
||||||
break;
|
break;
|
||||||
|
case MASK:
|
||||||
|
reinterpret_cast<mask_t *>(data)->~mask_t();
|
||||||
|
break;
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
checked_delete(*reinterpret_cast<sequence_t **>(data));
|
checked_delete(*reinterpret_cast<sequence_t **>(data));
|
||||||
break;
|
break;
|
||||||
|
|
@ -134,7 +142,6 @@ void value_t::initialize()
|
||||||
false_value->type = BOOLEAN;
|
false_value->type = BOOLEAN;
|
||||||
*reinterpret_cast<bool *>(false_value->data) = false;
|
*reinterpret_cast<bool *>(false_value->data) = false;
|
||||||
|
|
||||||
#if 0
|
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(date_t));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(date_t));
|
||||||
|
|
@ -143,9 +150,9 @@ void value_t::initialize()
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_pair_t *));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
|
||||||
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(mask_t));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
|
||||||
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
|
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
|
||||||
#endif
|
|
||||||
|
|
||||||
DEBUG_(std::setw(3) << std::right << sizeof(bool)
|
DEBUG_(std::setw(3) << std::right << sizeof(bool)
|
||||||
<< " sizeof(bool)");
|
<< " sizeof(bool)");
|
||||||
|
|
@ -163,6 +170,8 @@ void value_t::initialize()
|
||||||
<< " sizeof(balance_pair_t *)");
|
<< " sizeof(balance_pair_t *)");
|
||||||
DEBUG_(std::setw(3) << std::right << sizeof(string)
|
DEBUG_(std::setw(3) << std::right << sizeof(string)
|
||||||
<< " sizeof(string)");
|
<< " sizeof(string)");
|
||||||
|
DEBUG_(std::setw(3) << std::right << sizeof(mask_t)
|
||||||
|
<< " sizeof(mask_t)");
|
||||||
DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
|
DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
|
||||||
<< " sizeof(sequence_t *)");
|
<< " sizeof(sequence_t *)");
|
||||||
DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
|
DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
|
||||||
|
|
@ -203,6 +212,8 @@ value_t::operator bool() const
|
||||||
return as_balance_pair();
|
return as_balance_pair();
|
||||||
case STRING:
|
case STRING:
|
||||||
return ! as_string().empty();
|
return ! as_string().empty();
|
||||||
|
case MASK:
|
||||||
|
return ! as_mask().empty();
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
return ! as_sequence().empty();
|
return ! as_sequence().empty();
|
||||||
case POINTER:
|
case POINTER:
|
||||||
|
|
@ -303,6 +314,17 @@ string value_t::to_string() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mask_t value_t::to_mask() const
|
||||||
|
{
|
||||||
|
if (is_mask()) {
|
||||||
|
return as_mask();
|
||||||
|
} else {
|
||||||
|
value_t temp(*this);
|
||||||
|
temp.in_place_cast(MASK);
|
||||||
|
return temp.as_mask();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
value_t::sequence_t value_t::to_sequence() const
|
value_t::sequence_t value_t::to_sequence() const
|
||||||
{
|
{
|
||||||
if (is_sequence()) {
|
if (is_sequence()) {
|
||||||
|
|
@ -922,6 +944,15 @@ bool value_t::is_equal_to(const value_t& val) const
|
||||||
case STRING:
|
case STRING:
|
||||||
if (val.is_string())
|
if (val.is_string())
|
||||||
return as_string() == val.as_string();
|
return as_string() == val.as_string();
|
||||||
|
else if (val.is_mask())
|
||||||
|
return val.as_mask().match(as_string());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MASK:
|
||||||
|
if (val.is_mask())
|
||||||
|
return as_mask() == val.as_mask();
|
||||||
|
else if (val.is_string())
|
||||||
|
return as_mask().match(val.as_string());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
|
|
@ -1257,6 +1288,12 @@ void value_t::in_place_not()
|
||||||
case BALANCE_PAIR:
|
case BALANCE_PAIR:
|
||||||
set_boolean(! as_balance_pair());
|
set_boolean(! as_balance_pair());
|
||||||
return;
|
return;
|
||||||
|
case STRING:
|
||||||
|
set_boolean(as_string().empty());
|
||||||
|
return;
|
||||||
|
case MASK:
|
||||||
|
set_boolean(as_mask().empty());
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1283,6 +1320,8 @@ bool value_t::is_realzero() const
|
||||||
return as_balance_pair().is_realzero();
|
return as_balance_pair().is_realzero();
|
||||||
case STRING:
|
case STRING:
|
||||||
return as_string().empty();
|
return as_string().empty();
|
||||||
|
case MASK:
|
||||||
|
return as_mask().empty();
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
return as_sequence().empty();
|
return as_sequence().empty();
|
||||||
|
|
||||||
|
|
@ -1314,6 +1353,8 @@ bool value_t::is_zero() const
|
||||||
return as_balance_pair().is_zero();
|
return as_balance_pair().is_zero();
|
||||||
case STRING:
|
case STRING:
|
||||||
return as_string().empty();
|
return as_string().empty();
|
||||||
|
case MASK:
|
||||||
|
return as_mask().empty();
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
return as_sequence().empty();
|
return as_sequence().empty();
|
||||||
|
|
||||||
|
|
@ -1505,6 +1546,7 @@ value_t value_t::strip_annotations(const bool keep_price,
|
||||||
case DATETIME:
|
case DATETIME:
|
||||||
case DATE:
|
case DATE:
|
||||||
case STRING:
|
case STRING:
|
||||||
|
case MASK:
|
||||||
case POINTER:
|
case POINTER:
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
|
|
@ -1625,6 +1667,10 @@ void value_t::dump(std::ostream& out, const int first_width,
|
||||||
out << as_string();
|
out << as_string();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MASK:
|
||||||
|
out << as_mask();
|
||||||
|
break;
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
out << boost::unsafe_any_cast<const void *>(&as_any_pointer());
|
out << boost::unsafe_any_cast<const void *>(&as_any_pointer());
|
||||||
break;
|
break;
|
||||||
|
|
@ -1700,6 +1746,10 @@ void value_t::print(std::ostream& out, const bool relaxed) const
|
||||||
out << '"' << as_string() << '"';
|
out << '"' << as_string() << '"';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MASK:
|
||||||
|
out << '/' << as_mask() << '/';
|
||||||
|
break;
|
||||||
|
|
||||||
case POINTER:
|
case POINTER:
|
||||||
assert(false);
|
assert(false);
|
||||||
break;
|
break;
|
||||||
|
|
@ -1801,7 +1851,7 @@ void value_t::write(std::ostream& out) const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw_(value_error, "Cannot read " << label() << " to a stream");
|
throw_(value_error, "Cannot write " << label() << " to a stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
void value_t::write_xml(std::ostream& out, const int depth) const
|
void value_t::write_xml(std::ostream& out, const int depth) const
|
||||||
|
|
@ -1846,6 +1896,11 @@ void value_t::write_xml(std::ostream& out, const int depth) const
|
||||||
<< as_string()
|
<< as_string()
|
||||||
<< "</string>\n";
|
<< "</string>\n";
|
||||||
break;
|
break;
|
||||||
|
case MASK:
|
||||||
|
out << xml_str("<mask>", depth + 1)
|
||||||
|
<< as_mask()
|
||||||
|
<< "</mask>\n";
|
||||||
|
break;
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
out << xml_str("<sequence>\n", depth + 1);
|
out << xml_str("<sequence>\n", depth + 1);
|
||||||
foreach (const value_t& v, as_sequence())
|
foreach (const value_t& v, as_sequence())
|
||||||
|
|
|
||||||
35
src/value.h
35
src/value.h
|
|
@ -50,6 +50,7 @@
|
||||||
#define _VALUE_H
|
#define _VALUE_H
|
||||||
|
|
||||||
#include "balpair.h" // pulls in balance.h and amount.h
|
#include "balpair.h" // pulls in balance.h and amount.h
|
||||||
|
#include "mask.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -108,6 +109,7 @@ public:
|
||||||
BALANCE, // a ledger::balance_t
|
BALANCE, // a ledger::balance_t
|
||||||
BALANCE_PAIR, // a ledger::balance_pair_t
|
BALANCE_PAIR, // a ledger::balance_pair_t
|
||||||
STRING, // a string object
|
STRING, // a string object
|
||||||
|
MASK, // a regular expression mask
|
||||||
SEQUENCE, // a vector of value_t objects
|
SEQUENCE, // a vector of value_t objects
|
||||||
POINTER // an opaque pointer of any type
|
POINTER // an opaque pointer of any type
|
||||||
};
|
};
|
||||||
|
|
@ -299,6 +301,10 @@ public:
|
||||||
TRACE_CTOR(value_t, "const balance_pair_t&");
|
TRACE_CTOR(value_t, "const balance_pair_t&");
|
||||||
set_balance_pair(val);
|
set_balance_pair(val);
|
||||||
}
|
}
|
||||||
|
value_t(const mask_t& val) {
|
||||||
|
TRACE_CTOR(value_t, "const mask_t&");
|
||||||
|
set_mask(val);
|
||||||
|
}
|
||||||
|
|
||||||
explicit value_t(const string& val, bool literal = false) {
|
explicit value_t(const string& val, bool literal = false) {
|
||||||
TRACE_CTOR(value_t, "const string&, bool");
|
TRACE_CTOR(value_t, "const string&, bool");
|
||||||
|
|
@ -477,6 +483,7 @@ public:
|
||||||
* is_balance()
|
* is_balance()
|
||||||
* is_balance_pair()
|
* is_balance_pair()
|
||||||
* is_string()
|
* is_string()
|
||||||
|
* is_mask()
|
||||||
* is_sequence()
|
* is_sequence()
|
||||||
* is_pointer()
|
* is_pointer()
|
||||||
*
|
*
|
||||||
|
|
@ -649,6 +656,27 @@ public:
|
||||||
new(reinterpret_cast<string *>(storage->data)) string(val);
|
new(reinterpret_cast<string *>(storage->data)) string(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_mask() const {
|
||||||
|
return is_type(MASK);
|
||||||
|
}
|
||||||
|
mask_t& as_mask_lval() {
|
||||||
|
assert(is_mask());
|
||||||
|
_dup();
|
||||||
|
return *reinterpret_cast<mask_t *>(storage->data);
|
||||||
|
}
|
||||||
|
const mask_t& as_mask() const {
|
||||||
|
assert(is_mask());
|
||||||
|
return *reinterpret_cast<mask_t *>(storage->data);
|
||||||
|
}
|
||||||
|
void set_mask(const string& val) {
|
||||||
|
set_type(MASK);
|
||||||
|
new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val);
|
||||||
|
}
|
||||||
|
void set_mask(const mask_t& val) {
|
||||||
|
set_type(MASK);
|
||||||
|
new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val);
|
||||||
|
}
|
||||||
|
|
||||||
bool is_sequence() const {
|
bool is_sequence() const {
|
||||||
return is_type(SEQUENCE);
|
return is_type(SEQUENCE);
|
||||||
}
|
}
|
||||||
|
|
@ -731,6 +759,7 @@ public:
|
||||||
balance_t to_balance() const;
|
balance_t to_balance() const;
|
||||||
balance_pair_t to_balance_pair() const;
|
balance_pair_t to_balance_pair() const;
|
||||||
string to_string() const;
|
string to_string() const;
|
||||||
|
mask_t to_mask() const;
|
||||||
sequence_t to_sequence() const;
|
sequence_t to_sequence() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -879,6 +908,8 @@ public:
|
||||||
return "a balance pair";
|
return "a balance pair";
|
||||||
case STRING:
|
case STRING:
|
||||||
return "a string";
|
return "a string";
|
||||||
|
case MASK:
|
||||||
|
return "a regexp";
|
||||||
case SEQUENCE:
|
case SEQUENCE:
|
||||||
return "a sequence";
|
return "a sequence";
|
||||||
case POINTER:
|
case POINTER:
|
||||||
|
|
@ -928,6 +959,10 @@ inline value_t string_value(const string& str) {
|
||||||
return value_t(str, true);
|
return value_t(str, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline value_t mask_value(const string& str) {
|
||||||
|
return value_t(mask_t(str));
|
||||||
|
}
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
|
inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
|
||||||
val.print(out, 12);
|
val.print(out, 12);
|
||||||
return out;
|
return out;
|
||||||
|
|
|
||||||
16
src/xact.cc
16
src/xact.cc
|
|
@ -64,6 +64,10 @@ item_t::state_t xact_t::state() const
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
value_t get_entry(xact_t& xact) {
|
||||||
|
return value_t(static_cast<scope_t *>(xact.entry));
|
||||||
|
}
|
||||||
|
|
||||||
value_t get_code(xact_t& xact) {
|
value_t get_code(xact_t& xact) {
|
||||||
if (xact.entry->code)
|
if (xact.entry->code)
|
||||||
return string_value(*xact.entry->code);
|
return string_value(*xact.entry->code);
|
||||||
|
|
@ -172,6 +176,11 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_cost>);
|
return WRAP_FUNCTOR(get_wrapper<&get_cost>);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'e':
|
||||||
|
if (name == "entry")
|
||||||
|
return WRAP_FUNCTOR(get_wrapper<&get_entry>);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
if (name == "payee")
|
if (name == "payee")
|
||||||
return WRAP_FUNCTOR(get_wrapper<&get_payee>);
|
return WRAP_FUNCTOR(get_wrapper<&get_payee>);
|
||||||
|
|
@ -189,14 +198,7 @@ expr_t::ptr_op_t xact_t::lookup(const string& name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
// jww (2008-09-19): I don't think we can lookup in entry, because
|
|
||||||
// that means the functor returned would be expecting an entry to be
|
|
||||||
// passed to it, rather than a transaction.
|
|
||||||
return entry->lookup(name);
|
|
||||||
#else
|
|
||||||
return item_t::lookup(name);
|
return item_t::lookup(name);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool xact_t::valid() const
|
bool xact_t::valid() const
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue