Completely revised the way XPath expressions are calculated.

This commit is contained in:
John Wiegley 2007-05-19 02:58:38 +00:00
parent 2d8512af88
commit b6ab7deb63
16 changed files with 1033 additions and 1340 deletions

View file

@ -126,13 +126,13 @@ commodity_t::operator bool() const
annotated_commodity_t& commodity_t::as_annotated() annotated_commodity_t& commodity_t::as_annotated()
{ {
assert(annotated); assert(annotated);
return *polymorphic_downcast<annotated_commodity_t *>(this); return downcast<annotated_commodity_t>(*this);
} }
const annotated_commodity_t& commodity_t::as_annotated() const const annotated_commodity_t& commodity_t::as_annotated() const
{ {
assert(annotated); assert(annotated);
return *polymorphic_downcast<const annotated_commodity_t *>(this); return downcast<const annotated_commodity_t>(*this);
} }
bool commodity_t::symbol_needs_quotes(const string& symbol) bool commodity_t::symbol_needs_quotes(const string& symbol)

View file

@ -136,7 +136,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
else else
#endif #endif
if (verb == "xml") if (verb == "xml")
command = xml_command(); command = bind(xml_command, _1);
else if (verb == "expr") else if (verb == "expr")
; ;
else if (verb == "xpath") else if (verb == "xpath")
@ -145,17 +145,19 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
xml::xpath_t expr(*arg); xml::xpath_t expr(*arg);
xml::document_t temp(xml::LEDGER_NODE); xml::document_t temp(xml::LEDGER_NODE);
xml::xpath_t::document_scope_t doc_scope(report, temp);
IF_INFO() { IF_INFO() {
std::cout << "Value expression tree:" << std::endl; std::cout << "Value expression tree:" << std::endl;
expr.dump(std::cout); expr.dump(std::cout);
std::cout << std::endl; std::cout << std::endl;
std::cout << "Value expression parsed was:" << std::endl; std::cout << "Value expression parsed was:" << std::endl;
expr.print(std::cout, temp); expr.print(std::cout, doc_scope);
std::cout << std::endl << std::endl; std::cout << std::endl << std::endl;
std::cout << "Result of calculation: "; std::cout << "Result of calculation: ";
} }
std::cout << expr.calc(temp, report).strip_annotations() << std::endl; std::cout << expr.calc(doc_scope).strip_annotations() << std::endl;
return 0; return 0;
} }
@ -242,8 +244,12 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
} }
#endif #endif
report.define("ostream", value_t(out));
// Are we handling the expr commands? Do so now. // Are we handling the expr commands? Do so now.
xml::xpath_t::document_scope_t doc_scope(report, xml_document);
if (verb == "expr") { if (verb == "expr") {
xml::xpath_t expr(*arg); xml::xpath_t expr(*arg);
@ -252,12 +258,12 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
expr.dump(*out); expr.dump(*out);
*out << std::endl; *out << std::endl;
*out << "Value expression parsed was:" << std::endl; *out << "Value expression parsed was:" << std::endl;
expr.print(*out, xml_document); expr.print(*out, doc_scope);
*out << std::endl << std::endl; *out << std::endl << std::endl;
*out << "Result of calculation: "; *out << "Result of calculation: ";
} }
*out << expr.calc(xml_document, report).strip_annotations() << std::endl; *out << expr.calc(doc_scope).strip_annotations() << std::endl;
return 0; return 0;
} }
@ -265,9 +271,10 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
std::cout << "XPath parsed:" << std::endl; std::cout << "XPath parsed:" << std::endl;
xml::xpath_t xpath(*arg); xml::xpath_t xpath(*arg);
xpath.print(*out, xml_document); xpath.print(*out, doc_scope);
*out << std::endl; *out << std::endl;
#if 0
foreach (const value_t& value, xpath.find_all(xml_document, report)) { foreach (const value_t& value, xpath.find_all(xml_document, report)) {
if (value.is_xml_node()) { if (value.is_xml_node()) {
value.as_xml_node()->print(std::cout); value.as_xml_node()->print(std::cout);
@ -276,31 +283,27 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
} }
std::cout << std::endl; std::cout << std::endl;
} }
#endif
return 0; return 0;
} }
// Apply transforms to the hierarchical document structure // Apply transforms to the hierarchical document structure
INFO_START(transforms, "Applied transforms"); INFO_START(transforms, "Applied transforms");
report.apply_transforms(xml_document); report.apply_transforms(doc_scope);
INFO_FINISH(transforms); INFO_FINISH(transforms);
// Create an argument scope containing the report command's // Create an argument scope containing the report command's
// arguments, and then invoke the command. // arguments, and then invoke the command.
xml::xpath_t::scope_t locals(report, xml::xpath_t::scope_t::ARGUMENT); xml::xpath_t::call_scope_t command_args(doc_scope);
locals.args.push_back(out); for (strings_list::iterator i = arg; i != args.end(); i++)
locals.args.push_back(&xml_document); command_args.push_back(value_t(*i, true));
value_t::sequence_t args_list;
foreach (string& i, args)
args_list.push_back(value_t(i, true));
locals.args.push_back(args_list);
INFO_START(command, "Did user command '" << verb << "'"); INFO_START(command, "Did user command '" << verb << "'");
command(locals); command(command_args);
INFO_FINISH(command); INFO_FINISH(command);

View file

@ -40,6 +40,15 @@ const char * node_t::name() const
return *document().lookup_name(name_id()); return *document().lookup_name(name_id());
} }
optional<const string&> node_t::get_attr(const string& _name) const
{
optional<nameid_t> name_id = document().lookup_name_id(_name);
if (name_id)
return get_attr(*name_id);
else
return none;
}
void output_xml_string(std::ostream& out, const string& str) void output_xml_string(std::ostream& out, const string& str)
{ {
for (const char * s = str.c_str(); *s; s++) { for (const char * s = str.c_str(); *s; s++) {

View file

@ -87,12 +87,12 @@ public:
parent_node_t& as_parent_node() { parent_node_t& as_parent_node() {
if (! is_parent_node()) if (! is_parent_node())
throw_(std::logic_error, "Request to cast leaf node to a parent node"); throw_(std::logic_error, "Request to cast leaf node to a parent node");
return *polymorphic_downcast<parent_node_t *>(this); return downcast<parent_node_t>(*this);
} }
const parent_node_t& as_parent_node() const { const parent_node_t& as_parent_node() const {
if (! is_parent_node()) if (! is_parent_node())
throw_(std::logic_error, "Request to cast leaf node to a parent node"); throw_(std::logic_error, "Request to cast leaf node to a parent node");
return *polymorphic_downcast<const parent_node_t *>(this); return downcast<const parent_node_t>(*this);
} }
virtual value_t to_value() const = 0; virtual value_t to_value() const = 0;
@ -116,6 +116,7 @@ public:
attributes = attributes_t(); attributes = attributes_t();
attributes->push_back(attr_pair(_name_id, value)); attributes->push_back(attr_pair(_name_id, value));
} }
optional<const string&> get_attr(const string& _name) const;
optional<const string&> get_attr(const nameid_t _name_id) const { optional<const string&> get_attr(const nameid_t _name_id) const {
if (attributes) { if (attributes) {
typedef attributes_t::nth_index<1>::type attributes_by_name; typedef attributes_t::nth_index<1>::type attributes_by_name;

View file

@ -82,11 +82,11 @@ namespace {
#if 0 #if 0
try { try {
#endif #endif
xml::xpath_t::scope_t arguments(scope, xml::xpath_t::scope_t::ARGUMENT); xml::xpath_t::call_scope_t args(scope);
if (arg) if (arg)
arguments.args.push_back(value_t(arg, true)); args.push_back(value_t(arg, true));
opt(arguments); opt(args);
#if 0 #if 0
} }
catch (error * err) { catch (error * err) {

View file

@ -87,7 +87,7 @@ struct python_run
}; };
python_interpreter_t::python_interpreter_t(xml::xpath_t::scope_t& parent) python_interpreter_t::python_interpreter_t(xml::xpath_t::scope_t& parent)
: xml::xpath_t::scope_t(parent), : xml::xpath_t::symbol_scope_t(parent),
mmodule(borrowed(PyImport_AddModule("__main__"))), mmodule(borrowed(PyImport_AddModule("__main__"))),
nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get())))) nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get()))))
{ {
@ -176,16 +176,20 @@ object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
return object(); return object();
} }
value_t python_interpreter_t::functor_t::operator()(xml::xpath_t::scope_t& locals) value_t python_interpreter_t::functor_t::operator()
(xml::xpath_t::call_scope_t& args)
{ {
try { try {
if (! PyCallable_Check(func.ptr())) { if (! PyCallable_Check(func.ptr())) {
return extract<value_t>(func.ptr()); return extract<value_t>(func.ptr());
} else { } else {
if (locals.args.size() > 0) { if (args.size() > 0) {
list arglist; list arglist;
foreach (const value_t& value, locals.args) if (args.value().is_sequence())
arglist.append(value); foreach (const value_t& value, args.value().as_sequence())
arglist.append(value);
else
arglist.append(args.value());
if (PyObject * val = if (PyObject * val =
PyObject_CallObject(func.ptr(), PyObject_CallObject(func.ptr(),
@ -215,11 +219,11 @@ value_t python_interpreter_t::functor_t::operator()(xml::xpath_t::scope_t& local
} }
value_t python_interpreter_t::lambda_t::operator() value_t python_interpreter_t::lambda_t::operator()
(xml::xpath_t::scope_t& locals) (xml::xpath_t::call_scope_t& args)
{ {
try { try {
assert(locals.args.size() == 1); assert(args.size() == 1);
value_t item = locals.args[0]; value_t item = args[0];
assert(item.is_xml_node()); assert(item.is_xml_node());
return call<value_t>(func.ptr(), item.as_xml_node()); return call<value_t>(func.ptr(), item.as_xml_node());
} }

View file

@ -29,8 +29,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#ifndef _PY_EVAL_H #ifndef _PYINTERP_H
#define _PY_EVAL_H #define _PYINTERP_H
#include "xpath.h" #include "xpath.h"
@ -39,7 +39,7 @@
namespace ledger { namespace ledger {
class python_interpreter_t : public xml::xpath_t::scope_t class python_interpreter_t : public xml::xpath_t::symbol_scope_t
{ {
boost::python::handle<> mmodule; boost::python::handle<> mmodule;
@ -76,28 +76,23 @@ class python_interpreter_t : public xml::xpath_t::scope_t
public: public:
functor_t(const string& name, boost::python::object _func) : func(_func) {} functor_t(const string& name, boost::python::object _func) : func(_func) {}
virtual ~functor_t() {} virtual ~functor_t() {}
virtual value_t operator()(xml::xpath_t::scope_t& locals); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
virtual void define(const string& name, xml::xpath_t::ptr_op_t def) {
// Pass any definitions up to our parent
parent->define(name, def);
}
virtual xml::xpath_t::ptr_op_t lookup(const string& name) { virtual xml::xpath_t::ptr_op_t lookup(const string& name) {
if (boost::python::object func = eval(name)) if (boost::python::object func = eval(name))
return xml::xpath_t::wrap_functor(functor_t(name, func)); return WRAP_FUNCTOR(functor_t(name, func));
else else
return parent ? parent->lookup(name) : NULL; return xml::xpath_t::symbol_scope_t::lookup(name);
} }
class lambda_t : public functor_t { class lambda_t : public functor_t {
public: public:
lambda_t(boost::python::object code) : functor_t("<lambda>", code) {} lambda_t(boost::python::object code) : functor_t("<lambda>", code) {}
virtual value_t operator()(xml::xpath_t::scope_t& locals); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
}; };
} // namespace ledger } // namespace ledger
#endif // _PY_EVAL_H #endif // _PYINTERP_H

View file

@ -38,41 +38,46 @@ report_t::~report_t()
TRACE_DTOR(report_t); TRACE_DTOR(report_t);
} }
void report_t::apply_transforms(xml::document_t& document) void report_t::apply_transforms(xml::xpath_t::scope_t& scope)
{ {
foreach (transform_t& transform, transforms) typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple;
transform.execute(document);
foreach (transform_details_tuple& transform_details, transforms) {
xml::xpath_t::call_scope_t call_args(scope);
call_args.set_args(transform_details.get<1>());
(*transform_details.get<0>())(call_args);
}
} }
value_t report_t::abbrev(xml::xpath_t::scope_t& locals) value_t report_t::abbrev(xml::xpath_t::call_scope_t& args)
{ {
if (locals.args.size() < 2) if (args.size() < 2)
throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])"); throw_(std::logic_error, "usage: abbrev(STRING, WIDTH [, STYLE, ABBREV_LEN])");
string str = locals.args[0].as_string(); string str = args[0].as_string();
long wid = locals.args[1]; long wid = args[1];
elision_style_t style = session.elision_style; elision_style_t style = session.elision_style;
if (locals.args.size() == 3) if (args.size() == 3)
style = (elision_style_t)locals.args[2].as_long(); style = (elision_style_t)args[2].as_long();
long abbrev_len = session.abbrev_length; long abbrev_len = session.abbrev_length;
if (locals.args.size() == 4) if (args.size() == 4)
abbrev_len = locals.args[3].as_long(); abbrev_len = args[3].as_long();
return value_t(abbreviate(str, wid, style, true, (int)abbrev_len), true); return value_t(abbreviate(str, wid, style, true, (int)abbrev_len), true);
} }
value_t report_t::ftime(xml::xpath_t::scope_t& locals) value_t report_t::ftime(xml::xpath_t::call_scope_t& args)
{ {
if (locals.args.size() < 1) if (args.size() < 1)
throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])"); throw_(std::logic_error, "usage: ftime(DATE [, DATE_FORMAT])");
moment_t date = locals.args[0].as_datetime(); moment_t date = args[0].as_datetime();
string date_format; string date_format;
if (locals.args.size() == 2) if (args.size() == 2)
date_format = locals.args[1].as_string(); date_format = args[1].as_string();
#if 0 #if 0
// jww (2007-04-18): Need to setup an output facet here // jww (2007-04-18): Need to setup an output facet here
else else
@ -84,25 +89,27 @@ value_t report_t::ftime(xml::xpath_t::scope_t& locals)
#endif #endif
} }
#if 0
optional<value_t> optional<value_t>
report_t::resolve(const string& name, xml::xpath_t::scope_t& locals) report_t::resolve(const string& name, xml::xpath_t::call_scope_t& args)
{ {
const char * p = name.c_str(); const char * p = name.c_str();
switch (*p) { switch (*p) {
case 'a': case 'a':
if (name == "abbrev") { if (name == "abbrev") {
return abbrev(locals); return abbrev(args);
} }
break; break;
case 'f': case 'f':
if (name == "ftime") { if (name == "ftime") {
return ftime(locals); return ftime(args);
} }
break; break;
} }
return xml::xpath_t::scope_t::resolve(name, locals); return xml::xpath_t::scope_t::resolve(name, args);
} }
#endif
xml::xpath_t::ptr_op_t report_t::lookup(const string& name) xml::xpath_t::ptr_op_t report_t::lookup(const string& name)
{ {
@ -210,7 +217,7 @@ xml::xpath_t::ptr_op_t report_t::lookup(const string& name)
break; break;
} }
return xml::xpath_t::scope_t::lookup(name); return xml::xpath_t::symbol_scope_t::lookup(name);
} }
} // namespace ledger } // namespace ledger

View file

@ -39,9 +39,9 @@ namespace ledger {
typedef std::list<string> strings_list; typedef std::list<string> strings_list;
class report_t : public xml::xpath_t::scope_t class report_t : public xml::xpath_t::symbol_scope_t
{ {
public: public:
optional<path> output_file; optional<path> output_file;
string format_string; string format_string;
string amount_expr; string amount_expr;
@ -59,10 +59,10 @@ class report_t : public xml::xpath_t::scope_t
session_t& session; session_t& session;
transform_t * last_transform; transform_t * last_transform;
ptr_list<transform_t> transforms; std::list<tuple<shared_ptr<transform_t>, value_t> > transforms;
explicit report_t(session_t& _session) explicit report_t(session_t& _session)
: xml::xpath_t::scope_t(_session), : xml::xpath_t::symbol_scope_t(downcast<xml::xpath_t::scope_t>(_session)),
show_totals(false), show_totals(false),
raw_mode(false), raw_mode(false),
session(_session), session(_session),
@ -76,14 +76,14 @@ class report_t : public xml::xpath_t::scope_t
virtual ~report_t(); virtual ~report_t();
void apply_transforms(xml::document_t& document); void apply_transforms(xml::xpath_t::scope_t& scope);
// //
// Utility functions for value expressions // Utility functions for value expressions
// //
value_t ftime(xml::xpath_t::scope_t& locals); value_t ftime(xml::xpath_t::call_scope_t& args);
value_t abbrev(xml::xpath_t::scope_t& locals); value_t abbrev(xml::xpath_t::call_scope_t& args);
// //
// Config options // Config options
@ -94,36 +94,36 @@ class report_t : public xml::xpath_t::scope_t
xml::xpath_t(expr).compile((xml::document_t *)NULL, this); xml::xpath_t(expr).compile((xml::document_t *)NULL, this);
#endif #endif
} }
value_t option_eval(xml::xpath_t::scope_t& locals) { value_t option_eval(xml::xpath_t::call_scope_t& args) {
eval(locals.args[0].as_string()); eval(args[0].as_string());
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_amount(xml::xpath_t::scope_t& locals) { value_t option_amount(xml::xpath_t::call_scope_t& args) {
eval(string("t=") + locals.args[0].as_string()); eval(string("t=") + args[0].as_string());
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_total(xml::xpath_t::scope_t& locals) { value_t option_total(xml::xpath_t::call_scope_t& args) {
eval(string("T()=") + locals.args[0].as_string()); eval(string("T()=") + args[0].as_string());
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_format(xml::xpath_t::scope_t& locals) { value_t option_format(xml::xpath_t::call_scope_t& args) {
format_string = locals.args[0].as_string(); format_string = args[0].as_string();
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_raw(xml::xpath_t::scope_t& locals) { value_t option_raw(xml::xpath_t::call_scope_t& args) {
raw_mode = true; raw_mode = true;
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_foo(xml::xpath_t::scope_t& locals) { value_t option_foo(xml::xpath_t::call_scope_t& args) {
std::cout << "This is foo" << std::endl; std::cout << "This is foo" << std::endl;
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_bar(xml::xpath_t::scope_t& locals) { value_t option_bar(xml::xpath_t::call_scope_t& args) {
std::cout << "This is bar: " << locals.args[0] << std::endl; std::cout << "This is bar: " << args[0] << std::endl;
return NULL_VALUE; return NULL_VALUE;
} }
@ -132,44 +132,44 @@ class report_t : public xml::xpath_t::scope_t
// //
#if 0 #if 0
value_t option_select(xml::xpath_t::scope_t& locals) { value_t option_select(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new select_transform(locals.args[0].as_string())); transforms.push_back(new select_transform(args[0].as_string()));
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_limit(xml::xpath_t::scope_t& locals) { value_t option_limit(xml::xpath_t::call_scope_t& args) {
string expr = (string("//xact[") + string expr = (string("//xact[") +
locals.args[0].as_string() + "]"); args[0].as_string() + "]");
transforms.push_back(new select_transform(expr)); transforms.push_back(new select_transform(expr));
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_remove(xml::xpath_t::scope_t& locals) { value_t option_remove(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new remove_transform(locals.args[0].as_string())); transforms.push_back(new remove_transform(args[0].as_string()));
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_accounts(xml::xpath_t::scope_t& locals) { value_t option_accounts(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new accounts_transform); transforms.push_back(new accounts_transform);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_compact(xml::xpath_t::scope_t& locals) { value_t option_compact(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new compact_transform); transforms.push_back(new compact_transform);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_clean(xml::xpath_t::scope_t& locals) { value_t option_clean(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new clean_transform); transforms.push_back(new clean_transform);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_entries(xml::xpath_t::scope_t& locals) { value_t option_entries(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new entries_transform); transforms.push_back(new entries_transform);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_split(xml::xpath_t::scope_t& locals) { value_t option_split(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new split_transform); transforms.push_back(new split_transform);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_merge(xml::xpath_t::scope_t& locals) { value_t option_merge(xml::xpath_t::call_scope_t& args) {
transforms.push_back(new merge_transform); transforms.push_back(new merge_transform);
return NULL_VALUE; return NULL_VALUE;
} }
@ -179,8 +179,6 @@ class report_t : public xml::xpath_t::scope_t
// Scope members // Scope members
// //
virtual optional<value_t> resolve(const string& name,
xml::xpath_t::scope_t& locals);
virtual xml::xpath_t::ptr_op_t lookup(const string& name); virtual xml::xpath_t::ptr_op_t lookup(const string& name);
}; };

View file

@ -68,6 +68,56 @@ void release_session_context()
#endif #endif
} }
session_t::session_t()
: symbol_scope_t(),
register_format
("%((//entry)%{date} %-.20{payee}"
"%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"),
wide_register_format
("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
"%48|%-.38A %22.108t %!22.132T\n"),
print_format
#if 1
("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"),
#else
("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"),
#endif
balance_format
("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"),
equity_format
("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"),
plot_amount_format
("%D %(@S(@t))\n"),
plot_total_format
("%D %(@S(@T))\n"),
write_hdr_format
("%d %Y%C%P\n"),
write_xact_format
(" %-34W %12o%n\n"),
prices_format
("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"),
pricesdb_format
("P %[%Y/%m/%d %H:%M:%S] %A %t\n"),
pricing_leeway(24 * 3600),
download_quotes(false),
use_cache(false),
cache_dirty(false),
now(now),
elision_style(ABBREVIATE),
abbrev_length(2),
ansi_codes(false),
ansi_invert(false)
{
TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
}
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)
@ -173,6 +223,7 @@ std::size_t session_t::read_data(xml::builder_t& builder,
return entry_count; return entry_count;
} }
#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)
{ {
@ -203,6 +254,7 @@ session_t::resolve(const string& name, xml::xpath_t::scope_t& locals)
} }
return xml::xpath_t::scope_t::resolve(name, locals); return xml::xpath_t::scope_t::resolve(name, locals);
} }
#endif
xml::xpath_t::ptr_op_t session_t::lookup(const string& name) xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
{ {
@ -239,7 +291,7 @@ xml::xpath_t::ptr_op_t session_t::lookup(const string& name)
break; break;
} }
return xml::xpath_t::scope_t::lookup(name); return xml::xpath_t::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

View file

@ -39,7 +39,7 @@
namespace ledger { namespace ledger {
class session_t : public xml::xpath_t::scope_t class session_t : public xml::xpath_t::symbol_scope_t
{ {
public: public:
static session_t * current; static session_t * current;
@ -79,53 +79,7 @@ class session_t : public xml::xpath_t::scope_t
ptr_list<journal_t> journals; ptr_list<journal_t> journals;
ptr_list<parser_t> parsers; ptr_list<parser_t> parsers;
session_t() : session_t();
register_format
("%((//entry)%{date} %-.20{payee}"
"%((./xact)%32|%-22{abbrev(account, 22)} %12.67t %12.80T\n))"),
wide_register_format
("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
"%48|%-.38A %22.108t %!22.132T\n"),
print_format
#if 1
("%(/%(/%{date} %-.20{payee}\n%(: %-34{account} %12t\n)\n))"),
#else
("\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"),
#endif
balance_format
("%(/%(//%20t %{\" \" * rdepth}%{rname}\n))--------------------\n%20t\n"),
equity_format
("%((/)%{ftime(now, date_format)} %-.20{\"Opening Balance\"}\n%((.//account[value != 0]) %-34{fullname} %12{value}\n)\n)"),
plot_amount_format
("%D %(@S(@t))\n"),
plot_total_format
("%D %(@S(@T))\n"),
write_hdr_format
("%d %Y%C%P\n"),
write_xact_format
(" %-34W %12o%n\n"),
prices_format
("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"),
pricesdb_format
("P %[%Y/%m/%d %H:%M:%S] %A %t\n"),
pricing_leeway(24 * 3600),
download_quotes(false),
use_cache(false),
cache_dirty(false),
now(now),
elision_style(ABBREVIATE),
abbrev_length(2),
ansi_codes(false),
ansi_invert(false) {
TRACE_CTOR(session_t, "xml::xpath_t::scope_t&");
}
virtual ~session_t() { virtual ~session_t() {
TRACE_DTOR(session_t); TRACE_DTOR(session_t);
} }
@ -178,8 +132,6 @@ class session_t : public xml::xpath_t::scope_t
// Scope members // Scope members
// //
virtual optional<value_t> resolve(const string& name,
xml::xpath_t::scope_t& locals = NULL);
virtual xml::xpath_t::ptr_op_t lookup(const string& name); virtual xml::xpath_t::ptr_op_t lookup(const string& name);
// //
@ -208,19 +160,19 @@ class session_t : public xml::xpath_t::scope_t
// Option handlers // Option handlers
// //
value_t option_file_(xml::xpath_t::scope_t& locals) { value_t option_file_(xml::xpath_t::call_scope_t& args) {
assert(locals.args.size() == 1); assert(args.size() == 1);
data_file = locals.args[0].as_string(); data_file = args[0].as_string();
return NULL_VALUE; return NULL_VALUE;
} }
#if 0 #if 0
#if defined(USE_BOOST_PYTHON) #if defined(USE_BOOST_PYTHON)
value_t option_import_(xml::xpath_t::scope_t& locals) { value_t option_import_(xml::xpath_t::call_scope_t& args) {
python_import(optarg); python_import(optarg);
return NULL_VALUE; return NULL_VALUE;
} }
value_t option_import_stdin(xml::xpath_t::scope_t& locals) { value_t option_import_stdin(xml::xpath_t::call_scope_t& args) {
python_eval(std::cin, PY_EVAL_MULTI); python_eval(std::cin, PY_EVAL_MULTI);
return NULL_VALUE; return NULL_VALUE;
} }

View file

@ -39,38 +39,38 @@ namespace ledger {
class transform_t { class transform_t {
public: public:
virtual ~transform_t() {} virtual ~transform_t() {}
virtual void execute(xml::document_t& document) = 0; virtual value_t operator()(xml::xpath_t::scope_t& args) = 0;
}; };
class check_transform : public transform_t { class check_transform : public transform_t {
// --check checks the validity of the item list. // --check checks the validity of the item list.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class accounts_transform : public transform_t { class accounts_transform : public transform_t {
// --accounts transforms the report tree into an account-wise view. // --accounts transforms the report tree into an account-wise view.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class compact_transform : public transform_t { class compact_transform : public transform_t {
// --compact compacts an account tree to remove accounts with only // --compact compacts an account tree to remove accounts with only
// one child account. // one child account.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class clean_transform : public transform_t { class clean_transform : public transform_t {
// --clean clears out entries and accounts that have no contents. // --clean clears out entries and accounts that have no contents.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class entries_transform : public transform_t { class entries_transform : public transform_t {
// --entries transforms the report tree into an entries-wise view. // --entries transforms the report tree into an entries-wise view.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class optimize_transform : public transform_t { class optimize_transform : public transform_t {
@ -79,7 +79,7 @@ class optimize_transform : public transform_t {
// commodity (one the negative of the other), the amount of the // commodity (one the negative of the other), the amount of the
// second transaction will be nulled out. // second transaction will be nulled out.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class split_transform : public transform_t { class split_transform : public transform_t {
@ -89,7 +89,7 @@ class split_transform : public transform_t {
// useful before sorting, for exampel, in order to sort by // useful before sorting, for exampel, in order to sort by
// transaction instead of by entry. // transaction instead of by entry.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class merge_transform : public transform_t { class merge_transform : public transform_t {
@ -97,7 +97,7 @@ class merge_transform : public transform_t {
// which share the same entry will be merged into a group of // which share the same entry will be merged into a group of
// transactions under one reported entry. // transactions under one reported entry.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class combine_transform : public transform_t { class combine_transform : public transform_t {
@ -107,14 +107,14 @@ class combine_transform : public transform_t {
// will show the terminating date or a label that is characteristic // will show the terminating date or a label that is characteristic
// of the set). // of the set).
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class group_transform : public transform_t { class group_transform : public transform_t {
// --group groups all transactions that affect the same account // --group groups all transactions that affect the same account
// within an entry, so that they appear as a single transaction. // within an entry, so that they appear as a single transaction.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class collapse_transform : public transform_t { class collapse_transform : public transform_t {
@ -123,7 +123,7 @@ class collapse_transform : public transform_t {
// fictitous account "<total>" is used to represent the final sum, // fictitous account "<total>" is used to represent the final sum,
// if multiple accounts are involved. // if multiple accounts are involved.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class subtotal_transform : public transform_t { class subtotal_transform : public transform_t {
@ -131,7 +131,7 @@ class subtotal_transform : public transform_t {
// one giant entry. When used in conjunction with --group, the // one giant entry. When used in conjunction with --group, the
// affect is very similar to a regular balance report. // affect is very similar to a regular balance report.
public: public:
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
#if 0 #if 0
@ -146,7 +146,7 @@ class select_transform : public transform_t
} }
virtual ~select_transform() {} virtual ~select_transform() {}
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
class remove_transform : public select_transform class remove_transform : public select_transform
@ -155,7 +155,7 @@ class remove_transform : public select_transform
remove_transform(const string& selection_path) remove_transform(const string& selection_path)
: select_transform(selection_path) {} : select_transform(selection_path) {}
virtual void execute(xml::document_t& document); virtual value_t operator()(xml::xpath_t::call_scope_t& args);
}; };
#endif #endif

View file

@ -521,6 +521,11 @@ inline void throw_unexpected_error(char, char) {
namespace ledger { namespace ledger {
template <typename T, typename U>
inline T& downcast(U& object) {
return *polymorphic_downcast<T *>(&object);
}
path resolve_path(const path& pathname); path resolve_path(const path& pathname);
#ifdef HAVE_REALPATH #ifdef HAVE_REALPATH

View file

@ -61,6 +61,10 @@ class value_t
public: public:
typedef std::vector<value_t> sequence_t; typedef std::vector<value_t> sequence_t;
typedef sequence_t::iterator iterator;
typedef sequence_t::const_iterator const_iterator;
typedef sequence_t::difference_type difference_type;
enum type_t { enum type_t {
VOID, VOID,
BOOLEAN, BOOLEAN,
@ -72,7 +76,6 @@ public:
STRING, STRING,
SEQUENCE, SEQUENCE,
XML_NODE, XML_NODE,
CONST_XML_NODE,
POINTER POINTER
}; };
@ -217,12 +220,9 @@ public:
TRACE_CTOR(value_t, "xml::node_t *"); TRACE_CTOR(value_t, "xml::node_t *");
set_xml_node(xml_node); set_xml_node(xml_node);
} }
value_t(const xml::node_t * xml_node) { template <typename T>
TRACE_CTOR(value_t, "const xml::node_t *"); value_t(T * item) {
set_xml_node(xml_node); TRACE_CTOR(value_t, "T *");
}
value_t(void * item) {
TRACE_CTOR(value_t, "void *");
set_pointer(item); set_pointer(item);
} }
~value_t() { ~value_t() {
@ -254,11 +254,22 @@ public:
else else
storage->destroy(); storage->destroy();
} }
void _reset() {
if (storage) {
storage->destroy();
storage = intrusive_ptr<storage_t>();
}
}
operator bool() const; operator bool() const;
bool is_null() const { bool is_null() const {
return ! storage || is_type(VOID); if (! storage) {
return true;
} else {
assert(! is_type(VOID));
return false;
}
} }
type_t type() const { type_t type() const {
type_t result = storage ? storage->type : VOID; type_t result = storage ? storage->type : VOID;
@ -272,9 +283,14 @@ private:
} }
void set_type(type_t new_type) { void set_type(type_t new_type) {
assert(new_type >= VOID && new_type <= POINTER); assert(new_type >= VOID && new_type <= POINTER);
_clear(); if (new_type == VOID) {
storage->type = new_type; _reset();
assert(is_type(new_type)); assert(is_null());
} else {
_clear();
storage->type = new_type;
assert(is_type(new_type));
}
} }
public: public:
@ -415,36 +431,21 @@ public:
} }
bool is_xml_node() const { bool is_xml_node() const {
return is_type(XML_NODE) || is_type(CONST_XML_NODE); return is_type(XML_NODE);
} }
xml::node_t *& as_xml_node_lval() { xml::node_t *& as_xml_node_lval() {
assert(is_xml_node()); assert(is_xml_node());
assert(! is_type(CONST_XML_NODE));
_dup(); _dup();
return *(xml::node_t **) storage->data; return *(xml::node_t **) storage->data;
} }
xml::node_t * as_xml_node_mutable() { xml::node_t * as_xml_node() const {
assert(is_xml_node()); assert(is_xml_node());
assert(! is_type(CONST_XML_NODE));
return *(xml::node_t **) storage->data; return *(xml::node_t **) storage->data;
} }
const xml::node_t * as_xml_node() const {
assert(is_xml_node());
return *(const xml::node_t **) storage->data;
}
template <typename T>
T * as_xml_node() const {
assert(is_xml_node());
return *(T **) storage->data;
}
void set_xml_node(xml::node_t * val) { void set_xml_node(xml::node_t * val) {
set_type(XML_NODE); set_type(XML_NODE);
*(xml::node_t **) storage->data = val; *(xml::node_t **) storage->data = val;
} }
void set_xml_node(const xml::node_t * val) {
set_type(CONST_XML_NODE);
*(const xml::node_t **) storage->data = val;
}
bool is_pointer() const { bool is_pointer() const {
return is_type(POINTER); return is_type(POINTER);
@ -460,6 +461,12 @@ public:
_dup(); _dup();
return any_cast<T *>(*(boost::any *) storage->data); return any_cast<T *>(*(boost::any *) storage->data);
} }
template <typename T>
T& as_ref_lval() {
assert(is_pointer());
_dup();
return *any_cast<T *>(*(boost::any *) storage->data);
}
boost::any as_any_pointer() const { boost::any as_any_pointer() const {
assert(is_pointer()); assert(is_pointer());
return *(boost::any *) storage->data; return *(boost::any *) storage->data;
@ -469,6 +476,11 @@ public:
assert(is_pointer()); assert(is_pointer());
return any_cast<T *>(*(boost::any *) storage->data); return any_cast<T *>(*(boost::any *) storage->data);
} }
template <typename T>
T& as_ref() const {
assert(is_pointer());
return *any_cast<T *>(*(boost::any *) storage->data);
}
void set_any_pointer(const boost::any& val) { void set_any_pointer(const boost::any& val) {
set_type(POINTER); set_type(POINTER);
new((boost::any *) storage->data) boost::any(val); new((boost::any *) storage->data) boost::any(val);
@ -496,18 +508,66 @@ public:
void in_place_simplify(); void in_place_simplify();
value_t& operator[](const int index) { value_t& operator[](const int index) {
return as_sequence_lval()[index]; assert(! is_null());
if (is_sequence())
return as_sequence_lval()[index];
else if (index == 0)
return *this;
assert(false);
static value_t null;
return null;
} }
const value_t& operator[](const int index) const { const value_t& operator[](const int index) const {
return as_sequence()[index]; assert(! is_null());
if (is_sequence())
return as_sequence()[index];
else if (index == 0)
return *this;
assert(false);
static value_t null;
return null;
} }
void push_back(const value_t& val) { void push_back(const value_t& val) {
return as_sequence_lval().push_back(val); if (is_null()) {
*this = val;
} else {
if (! is_sequence())
in_place_cast(SEQUENCE);
if (! val.is_sequence())
as_sequence_lval().push_back(val);
else
std::copy(val.as_sequence().begin(), val.as_sequence().end(),
as_sequence_lval().end());
}
}
void pop_back() {
assert(! is_null());
if (! is_sequence()) {
_reset();
} else {
as_sequence_lval().pop_back();
std::size_t new_size = as_sequence().size();
if (new_size == 0)
_reset();
else if (new_size == 1)
*this = as_sequence().front();
}
} }
const std::size_t size() const { const std::size_t size() const {
return as_sequence().size(); if (is_null())
return 0;
else if (is_sequence())
return as_sequence().size();
else
return 1;
} }
value_t& operator+=(const value_t& val); value_t& operator+=(const value_t& val);
@ -542,7 +602,6 @@ public:
case SEQUENCE: case SEQUENCE:
return "a sequence"; return "a sequence";
case XML_NODE: case XML_NODE:
case CONST_XML_NODE:
return "an xml node"; return "an xml node";
case POINTER: case POINTER:
return "a pointer"; return "a pointer";
@ -602,19 +661,12 @@ public:
friend std::ostream& operator<<(std::ostream& out, const value_t& val); friend std::ostream& operator<<(std::ostream& out, const value_t& val);
}; };
template <> #define NULL_VALUE (value_t())
inline const xml::node_t * value_t::as_xml_node() const {
assert(is_xml_node());
assert(! is_type(CONST_XML_NODE));
return *(const xml::node_t **) storage->data;
}
std::ostream& operator<<(std::ostream& out, const value_t& val); std::ostream& operator<<(std::ostream& out, const value_t& val);
DECLARE_EXCEPTION(value_error); DECLARE_EXCEPTION(value_error);
#define NULL_VALUE (value_t())
} // namespace ledger } // namespace ledger
#endif // _VALUE_H #endif // _VALUE_H

File diff suppressed because it is too large Load diff

View file

@ -51,77 +51,259 @@ public:
DECLARE_EXCEPTION(calc_error); DECLARE_EXCEPTION(calc_error);
public: public:
class scope_t; class call_scope_t;
typedef function<value_t (scope_t&)> function_t; typedef function<value_t (call_scope_t&)> function_t;
#define MAKE_FUNCTOR(x) \ #define MAKE_FUNCTOR(x) \
xml::xpath_t::wrap_functor(bind(&x, this, _1)) xml::xpath_t::op_t::wrap_functor(bind(&x, this, _1))
#define WRAP_FUNCTOR(x) \
static ptr_op_t wrap_value(const value_t& val); xml::xpath_t::op_t::wrap_functor(x)
static ptr_op_t wrap_functor(const function_t& fobj);
public: public:
class scope_t : public noncopyable class scope_t : public noncopyable
{ {
typedef std::map<const string, ptr_op_t> symbol_map;
symbol_map symbols;
public: public:
optional<scope_t&> parent; enum type_t {
value_t::sequence_t args; CHILD_SCOPE,
SYMBOL_SCOPE,
CALL_SCOPE,
CONTEXT_SCOPE,
NODE_SCOPE,
PREDICATE_SCOPE
} type_;
enum kind_t { NORMAL, STATIC, ARGUMENT } kind; explicit scope_t(type_t _type) : type_(_type) {
TRACE_CTOR(xpath_t::scope_t, "type_t");
explicit scope_t(const optional<scope_t&>& _parent = none,
kind_t _kind = NORMAL)
: parent(_parent), kind(_kind) {
TRACE_CTOR(xpath_t::scope_t, "kind_t, const optional<scope_t&>&");
}
explicit scope_t(scope_t& _parent, kind_t _kind = NORMAL)
: parent(_parent), kind(_kind) {
TRACE_CTOR(xpath_t::scope_t, "scope_t&, kind_t");
} }
virtual ~scope_t() { virtual ~scope_t() {
TRACE_DTOR(xpath_t::scope_t); TRACE_DTOR(xpath_t::scope_t);
} }
public: const type_t type() const {
virtual void define(const string& name, ptr_op_t def); return type_;
void define(const string& name, const function_t& def);
virtual ptr_op_t lookup(const string& name);
virtual optional<value_t> resolve(const string& name, scope_t& locals) {
if (parent)
return parent->resolve(name, locals);
return none;
} }
friend struct op_t; virtual void define(const string& name, ptr_op_t def) = 0;
void define(const string& name, const value_t& val);
virtual ptr_op_t lookup(const string& name) = 0;
value_t resolve(const string& name) {
return lookup(name)->calc(*this);
}
virtual optional<scope_t&> find_scope(const type_t _type,
bool skip_this = false) = 0;
template <typename T>
T& find_scope(bool skip_this = false) {
assert(false);
}
}; };
class function_scope_t : public scope_t class child_scope_t : public scope_t
{ {
const node_t& node; scope_t * parent;
std::size_t index;
std::size_t size;
public: public:
function_scope_t(const value_t::sequence_t& _sequence, explicit child_scope_t(type_t _type = CHILD_SCOPE)
const node_t& _node, : scope_t(_type), parent(NULL) {
std::size_t _index, TRACE_CTOR(xpath_t::child_scope_t, "type_t");
const optional<scope_t&>& _parent = none) }
: scope_t(_parent, STATIC), node(_node), index(_index), explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
size(_sequence.size()) {} : scope_t(_type), parent(&_parent) {
TRACE_CTOR(xpath_t::child_scope_t, "scope_t&, type_t");
}
virtual ~child_scope_t() {
TRACE_DTOR(xpath_t::child_scope_t);
}
public:
virtual void define(const string& name, ptr_op_t def) {
if (parent)
parent->define(name, def);
}
virtual ptr_op_t lookup(const string& name) {
if (parent)
return parent->lookup(name);
return ptr_op_t();
}
function_scope_t(const node_t& _node, virtual optional<scope_t&> find_scope(type_t _type,
std::size_t _index, bool skip_this = false) {
std::size_t _size, for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
const optional<scope_t&>& _parent = none) if (ptr->type() == _type)
: scope_t(_parent, STATIC), node(_node), index(_index), return *ptr;
size(_size) {}
virtual optional<value_t> resolve(const string& name, scope_t& locals); ptr = polymorphic_downcast<child_scope_t *>(ptr)->parent;
}
return none;
}
};
class symbol_scope_t : public child_scope_t
{
typedef std::map<const string, ptr_op_t> symbol_map;
symbol_map symbols;
public:
explicit symbol_scope_t()
: child_scope_t(SYMBOL_SCOPE) {
TRACE_CTOR(xpath_t::symbol_scope_t, "");
}
explicit symbol_scope_t(scope_t& _parent)
: child_scope_t(_parent, SYMBOL_SCOPE) {
TRACE_CTOR(xpath_t::symbol_scope_t, "scope_t&");
}
virtual ~symbol_scope_t() {
TRACE_DTOR(xpath_t::symbol_scope_t);
}
virtual void define(const string& name, ptr_op_t def);
void define(const string& name, const value_t& val) {
scope_t::define(name, val);
}
virtual ptr_op_t lookup(const string& name);
};
class call_scope_t : public child_scope_t
{
value_t args;
public:
explicit call_scope_t(scope_t& _parent)
: child_scope_t(_parent, CALL_SCOPE) {
TRACE_CTOR(xpath_t::call_scope_t, "scope_t&");
}
virtual ~call_scope_t() {
TRACE_DTOR(xpath_t::call_scope_t);
}
void set_args(const value_t& _args) {
args = _args;
}
value_t& value() {
return args;
}
value_t& operator[](const int index) {
return args[index];
}
const value_t& operator[](const int index) const {
return args[index];
}
void push_back(const value_t& val) {
args.push_back(val);
}
void pop_back() {
args.pop_back();
}
const std::size_t size() const {
return args.size();
}
};
class context_scope_t : public child_scope_t
{
public:
value_t element;
optional<value_t> sequence;
explicit context_scope_t(scope_t& _parent,
const value_t& _element,
const optional<value_t>& _sequence = none)
: child_scope_t(_parent, CONTEXT_SCOPE),
element(_element), sequence(_sequence)
{
TRACE_CTOR(xpath_t::context_scope_t,
"scope_t&, const value_t&, const optional<value_t>&");
assert(! element.is_sequence());
if (DO_VERIFY() && sequence) {
if (sequence->is_sequence()) {
value_t::sequence_t seq(sequence->as_sequence());
value_t::iterator i = std::find(seq.begin(), seq.end(), element);
assert(i != seq.end());
} else {
assert(element == *sequence);
}
}
}
virtual ~context_scope_t() {
TRACE_DTOR(xpath_t::context_scope_t);
}
const std::size_t index() const {
if (! sequence) {
return 0;
} else {
value_t::sequence_t seq(sequence->as_sequence());
value_t::iterator i = std::find(seq.begin(), seq.end(), element);
assert(i != seq.end());
int_least16_t offset = i - seq.begin();
assert(offset >= 0);
return std::size_t(offset);
}
}
const std::size_t size() const {
return sequence ? sequence->size() : (element.is_null() ? 0 : 1);
}
value_t& value() {
return element;
}
node_t& xml_node() {
if (! element.is_xml_node())
throw_(calc_error, "The current context value is not an XML node");
return *element.as_xml_node();
}
};
class node_scope_t : public context_scope_t
{
public:
node_scope_t(scope_t& _parent, node_t& _node)
: context_scope_t(_parent, &_node) {
TRACE_CTOR(xpath_t::node_scope_t, "scope_t&, node_t&");
type_ = NODE_SCOPE;
}
virtual ~node_scope_t() {
TRACE_DTOR(xpath_t::node_scope_t);
}
};
typedef node_scope_t document_scope_t;
class predicate_scope_t : public child_scope_t
{
public:
ptr_op_t predicate;
explicit predicate_scope_t(scope_t& _parent,
const ptr_op_t& _predicate)
: child_scope_t(_parent, PREDICATE_SCOPE), predicate(_predicate)
{
TRACE_CTOR(xpath_t::predicate_scope_t, "scope_t&, const ptr_op_t&");
}
virtual ~predicate_scope_t() {
TRACE_DTOR(xpath_t::predicate_scope_t);
}
bool test(scope_t& scope,
const value_t& val,
const optional<value_t>& sequence = none) const {
context_scope_t context_scope(scope, val, sequence);
if (predicate->is_value()) {
value_t& predicate_value(predicate->as_value());
if (predicate_value.is_long())
return predicate_value.as_long() == (long)context_scope.index() + 1;
}
return predicate->calc(context_scope).to_boolean();
}
}; };
#define XPATH_PARSE_NORMAL 0x00 #define XPATH_PARSE_NORMAL 0x00
@ -137,57 +319,59 @@ private:
struct token_t struct token_t
{ {
enum kind_t { enum kind_t {
IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
VALUE, // any kind of literal value VALUE, // any kind of literal value
AT_SYM, // @
IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
DOLLAR, // $ DOLLAR, // $
AT_SYM, // @
DOT, // . DOT, // .
DOTDOT, // .. DOTDOT, // ..
SLASH, // /
LPAREN, // ( LPAREN, // (
RPAREN, // ) RPAREN, // )
LBRACKET, // ( LBRACKET, // [
RBRACKET, // ) RBRACKET, // ]
EXCLAM, // !
NEQUAL, // !=
MINUS, // -
PLUS, // +
STAR, // *
POWER, // **
SLASH, // /
EQUAL, // = EQUAL, // =
ASSIGN, // := NEQUAL, // !=
LESS, // < LESS, // <
LESSEQ, // <= LESSEQ, // <=
GREATER, // > GREATER, // >
GREATEREQ, // >= GREATEREQ, // >=
AMPER, // &
PIPE, // | MINUS, // -
QUESTION, // ? PLUS, // +
COLON, // : STAR, // *
COMMA, // , KW_DIV,
EXCLAM, // !
KW_AND, KW_AND,
KW_OR, KW_OR,
KW_DIV,
KW_MOD, KW_MOD,
PIPE, // |
KW_UNION, KW_UNION,
COMMA, // ,
TOK_EOF, TOK_EOF,
UNKNOWN UNKNOWN
} kind; } kind;
char symbol[3]; char symbol[3];
value_t value; value_t value;
unsigned int length; std::size_t length;
token_t() : kind(UNKNOWN), length(0) { explicit token_t() : kind(UNKNOWN), length(0) {
TRACE_CTOR(xpath_t::token_t, ""); TRACE_CTOR(xpath_t::token_t, "");
} }
token_t(const token_t& other) { token_t(const token_t& other) {
assert(false); assert(false);
TRACE_CTOR(xpath_t::token_t, "copy"); TRACE_CTOR(xpath_t::token_t, "copy");
*this = other; *this = other;
} }
~token_t() { ~token_t() {
TRACE_DTOR(xpath_t::token_t); TRACE_DTOR(xpath_t::token_t);
} }
@ -202,7 +386,7 @@ private:
void clear() { void clear() {
kind = UNKNOWN; kind = UNKNOWN;
length = 0; length = 0;
value = 0L; value = NULL_VALUE;
symbol[0] = '\0'; symbol[0] = '\0';
symbol[1] = '\0'; symbol[1] = '\0';
@ -210,82 +394,23 @@ private:
} }
void parse_ident(std::istream& in); void parse_ident(std::istream& in);
void next(std::istream& in, flags_t flags); void next(std::istream& in, flags_t flags);
void rewind(std::istream& in); void rewind(std::istream& in);
void unexpected(); void unexpected();
static void unexpected(char c, char wanted = '\0'); static void unexpected(char c, char wanted = '\0');
}; };
public: public:
class path_t #if 0
{
public:
typedef function<void (const value_t&)> visitor_t;
typedef function<bool (const node_t&, scope_t&)> predicate_t;
private:
struct value_appender_t {
value_t::sequence_t& sequence;
value_appender_t(value_t::sequence_t& _sequence)
: sequence(_sequence) {}
void operator()(const value_t& val) {
sequence.push_back(val);
}
};
ptr_op_t path_expr;
template <typename NodeType>
void walk_elements(NodeType& start,
const ptr_op_t& element,
const bool recurse,
scope_t& scope,
const visitor_t& func);
template <typename NodeType>
void check_element(NodeType& start,
const ptr_op_t& element,
scope_t& scope,
std::size_t index,
std::size_t size,
const visitor_t& func);
public:
path_t(const xpath_t& xpath) : path_expr(xpath.ptr) {}
path_t(const ptr_op_t& _path_expr) : path_expr(_path_expr) {}
value_t find_all(node_t& start, scope_t& scope) {
value_t result = value_t::sequence_t();
visit(start, scope, value_appender_t(result.as_sequence_lval()));
return result;
}
value_t find_all(const node_t& start, scope_t& scope) {
value_t result = value_t::sequence_t();
visit(start, scope, value_appender_t(result.as_sequence_lval()));
return result;
}
void visit(node_t& start, scope_t& scope, const visitor_t& func) {
if (path_expr)
walk_elements<node_t>(start, path_expr, false, scope, func);
}
void visit(const node_t& start, scope_t& scope, const visitor_t& func) {
if (path_expr)
walk_elements<const node_t>(start, path_expr, false, scope, func);
}
};
template <typename NodeType>
class path_iterator_t class path_iterator_t
{ {
typedef NodeType * pointer; typedef NodeType * pointer;
typedef NodeType& reference; typedef NodeType& reference;
path_t path; path_t path;
reference start; node_t& start;
scope_t& scope; scope_t& scope;
mutable value_t::sequence_t sequence; mutable value_t::sequence_t sequence;
mutable bool searched; mutable bool searched;
@ -304,7 +429,7 @@ public:
typedef value_t::sequence_t::const_iterator const_iterator; typedef value_t::sequence_t::const_iterator const_iterator;
path_iterator_t(const xpath_t& path_expr, path_iterator_t(const xpath_t& path_expr,
reference _start, scope_t& _scope) node_t& _start, scope_t& _scope)
: path(path_expr), start(_start), scope(_scope), : path(path_expr), start(_start), scope(_scope),
searched(false) { searched(false) {
} }
@ -323,21 +448,21 @@ public:
iterator end() { return sequence.end(); } iterator end() { return sequence.end(); }
const_iterator end() const { return sequence.end(); } const_iterator end() const { return sequence.end(); }
}; };
#endif
struct op_t : public noncopyable struct op_t : public noncopyable
{ {
enum kind_t { enum kind_t {
VOID,
VALUE, VALUE,
FUNC_NAME,
VAR_NAME,
ARG_INDEX,
NODE_ID, NODE_ID,
NODE_NAME, NODE_NAME,
ATTR_ID, ATTR_ID,
ATTR_NAME, ATTR_NAME,
FUNC_NAME,
VAR_NAME,
ARG_INDEX,
CONSTANTS, // constants end here CONSTANTS, // constants end here
@ -345,15 +470,12 @@ public:
TERMINALS, // terminals end here TERMINALS, // terminals end here
O_NOT, O_CALL,
O_NEG, O_ARG,
O_UNION, O_FIND,
O_RFIND,
O_ADD, O_PRED,
O_SUB,
O_MUL,
O_DIV,
O_NEQ, O_NEQ,
O_EQ, O_EQ,
@ -362,22 +484,20 @@ public:
O_GT, O_GT,
O_GTE, O_GTE,
O_ADD,
O_SUB,
O_MUL,
O_DIV,
O_NEG,
O_NOT,
O_AND, O_AND,
O_OR, O_OR,
O_QUES, O_UNION,
O_COLON,
O_COMMA, O_COMMA,
O_DEFINE,
O_EVAL,
O_ARG,
O_FIND,
O_RFIND,
O_PRED,
LAST // operators end here LAST // operators end here
}; };
@ -387,13 +507,13 @@ public:
variant<unsigned int, // used by ARG_INDEX and O_ARG variant<unsigned int, // used by ARG_INDEX and O_ARG
value_t, // used by constant VALUE value_t, // used by constant VALUE
string, // used by constant SYMBOL string, // used by constants SYMBOL, *_NAME
function_t, // used by terminal FUNCTION function_t, // used by terminal FUNCTION
node_t::nameid_t, // used by NODE_NAME and ATTR_NAME node_t::nameid_t, // used by NODE_ID and ATTR_ID
ptr_op_t> // used by all binary operators ptr_op_t> // used by all binary operators
data; data;
op_t(const kind_t _kind) : kind(_kind), refc(0){ explicit op_t(const kind_t _kind) : kind(_kind), refc(0){
TRACE_CTOR(xpath_t::op_t, "const kind_t"); TRACE_CTOR(xpath_t::op_t, "const kind_t");
} }
~op_t() { ~op_t() {
@ -403,8 +523,6 @@ public:
assert(refc == 0); assert(refc == 0);
} }
op_t& operator=(const op_t&);
bool is_long() const { bool is_long() const {
return data.type() == typeid(unsigned int); return data.type() == typeid(unsigned int);
} }
@ -475,22 +593,6 @@ public:
data = val; data = val;
} }
#if 0
bool is_path() const {
return kind == PATH;
}
path_t& as_path() {
assert(kind == PATH);
return boost::get<path_t>(data);
}
const path_t& as_path() const {
return const_cast<op_t *>(this)->as_path();
}
void set_path(const path_t& val) {
data = val;
}
#endif
ptr_op_t& as_op() { ptr_op_t& as_op() {
assert(kind > TERMINALS); assert(kind > TERMINALS);
return boost::get<ptr_op_t>(data); return boost::get<ptr_op_t>(data);
@ -538,23 +640,38 @@ public:
data = expr; data = expr;
} }
static ptr_op_t new_node(kind_t kind, ptr_op_t left = NULL, static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = NULL,
ptr_op_t right = NULL); ptr_op_t _right = NULL);
ptr_op_t copy(ptr_op_t _left = NULL, ptr_op_t _right = NULL) const {
return new_node(kind, _left, _right);
}
ptr_op_t copy(ptr_op_t left = NULL, ptr_op_t right = NULL) const; static ptr_op_t wrap_value(const value_t& val);
ptr_op_t compile(const node_t& context, scope_t& scope, bool resolve = false); static ptr_op_t wrap_functor(const function_t& fobj);
void append_value(value_t::sequence_t& result_seq, value_t& value); ptr_op_t compile(scope_t& scope);
value_t current_value(scope_t& scope);
node_t& current_xml_node(scope_t& scope);
value_t calc(scope_t& scope);
static ptr_op_t defer_sequence(value_t::sequence_t& result_seq); struct print_context_t
{
scope_t& scope;
const bool relaxed;
const ptr_op_t& op_to_find;
unsigned long * start_pos;
unsigned long * end_pos;
bool print(std::ostream& out, print_context_t(scope_t& _scope,
document_t& document, const bool _relaxed = false,
const bool relaxed = true, const ptr_op_t& _op_to_find = ptr_op_t(),
const ptr_op_t& op_to_find = NULL, unsigned long * _start_pos = NULL,
unsigned long * start_pos = NULL, unsigned long * _end_pos = NULL)
unsigned long * end_pos = NULL) const; : scope(_scope), relaxed(_relaxed), op_to_find(_op_to_find),
start_pos(_start_pos), end_pos(_end_pos) {}
};
bool print(std::ostream& out, print_context_t& context) const;
void dump(std::ostream& out, const int depth) const; void dump(std::ostream& out, const int depth) const;
friend inline void intrusive_ptr_add_ref(xpath_t::op_t * op) { friend inline void intrusive_ptr_add_ref(xpath_t::op_t * op) {
@ -565,15 +682,12 @@ public:
} }
}; };
class op_predicate : public noncopyable class op_predicate : public noncopyable {
{
ptr_op_t op; ptr_op_t op;
public: public:
explicit op_predicate(ptr_op_t _op) : op(_op) {} explicit op_predicate(ptr_op_t _op) : op(_op) {}
bool operator()(scope_t& scope) const {
bool operator()(const node_t& node, scope_t& scope) { return op->calc(scope).to_boolean();
xpath_t result(op->compile(node, scope, true));
return result.ptr->as_value().to_boolean();
} }
}; };
@ -660,14 +774,9 @@ public:
return parse_expr(string(p), tflags); return parse_expr(string(p), tflags);
} }
bool print(std::ostream& out, bool print(std::ostream& out, op_t::print_context_t& context) const {
document_t& document,
const bool relaxed,
const ptr_op_t op_to_find,
unsigned long * start_pos,
unsigned long * end_pos) const {
if (ptr) if (ptr)
ptr->print(out, document, relaxed, op_to_find, start_pos, end_pos); ptr->print(out, context);
return true; return true;
} }
@ -675,20 +784,20 @@ public:
string expr; string expr;
flags_t flags; // flags used to parse `expr' flags_t flags; // flags used to parse `expr'
xpath_t() : ptr(NULL), use_lookahead(false), flags(0) { explicit xpath_t() : ptr(NULL), use_lookahead(false), flags(0) {
TRACE_CTOR(xpath_t, ""); TRACE_CTOR(xpath_t, "");
} }
xpath_t(ptr_op_t _ptr) : ptr(_ptr), use_lookahead(false) { explicit xpath_t(ptr_op_t _ptr) : ptr(_ptr), use_lookahead(false) {
TRACE_CTOR(xpath_t, "ptr_op_t"); TRACE_CTOR(xpath_t, "ptr_op_t");
} }
xpath_t(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) explicit xpath_t(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED)
: ptr(NULL), use_lookahead(false), flags(0) { : ptr(NULL), use_lookahead(false), flags(0) {
TRACE_CTOR(xpath_t, "const string&, flags_t"); TRACE_CTOR(xpath_t, "const string&, flags_t");
if (! _expr.empty()) if (! _expr.empty())
parse(_expr, _flags); parse(_expr, _flags);
} }
xpath_t(std::istream& in, flags_t _flags = XPATH_PARSE_RELAXED) explicit xpath_t(std::istream& in, flags_t _flags = XPATH_PARSE_RELAXED)
: ptr(NULL), use_lookahead(false), flags(0) { : ptr(NULL), use_lookahead(false), flags(0) {
TRACE_CTOR(xpath_t, "std::istream&, flags_t"); TRACE_CTOR(xpath_t, "std::istream&, flags_t");
parse(in, _flags); parse(in, _flags);
@ -698,33 +807,29 @@ public:
expr(other.expr), flags(other.flags) { expr(other.expr), flags(other.flags) {
TRACE_CTOR(xpath_t, "copy"); TRACE_CTOR(xpath_t, "copy");
} }
virtual ~xpath_t() { ~xpath_t() {
TRACE_DTOR(xpath_t); TRACE_DTOR(xpath_t);
} }
#if 0
xpath_t& operator=(const string& _expr) { xpath_t& operator=(const string& _expr) {
parse(_expr); parse(_expr);
return *this; return *this;
} }
#endif
xpath_t& operator=(const xpath_t& _expr); xpath_t& operator=(const xpath_t& _expr);
xpath_t& operator=(xpath_t& _xpath) {
ptr = _xpath.ptr;
expr = _xpath.expr;
flags = _xpath.flags;
use_lookahead = false;
return *this;
}
#if 0
operator ptr_op_t() throw() { operator ptr_op_t() throw() {
return ptr; return ptr;
} }
operator bool() const throw() { operator bool() const throw() {
return ptr != NULL; return ptr != NULL;
} }
operator string() const throw() { operator string() const throw() {
return expr; return expr;
} }
#endif
void parse(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) { void parse(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) {
expr = _expr; expr = _expr;
@ -737,18 +842,22 @@ public:
ptr = parse_expr(in, _flags); ptr = parse_expr(in, _flags);
} }
void compile(const node_t& context, scope_t& scope) { void compile(scope_t& scope) {
if (ptr.get()) if (ptr.get())
ptr = ptr->compile(context, scope); ptr = ptr->compile(scope);
} }
virtual value_t calc(const node_t& context, scope_t& scope) const; value_t calc(scope_t& scope) const {
if (ptr.get())
static value_t eval(const string& _expr, const node_t& context, return ptr->calc(scope);
scope_t& scope) { return NULL_VALUE;
return xpath_t(_expr).calc(context, scope);
} }
static value_t eval(const string& _expr, scope_t& scope) {
return xpath_t(_expr).calc(scope);
}
#if 0
path_iterator_t<node_t> path_iterator_t<node_t>
find_all(node_t& start, scope_t& scope) { find_all(node_t& start, scope_t& scope) {
return path_iterator_t<node_t>(*this, start, scope); return path_iterator_t<node_t>(*this, start, scope);
@ -765,47 +874,81 @@ public:
path_t::visitor_t& func) { path_t::visitor_t& func) {
path_t(*this).visit(start, scope, func); path_t(*this).visit(start, scope, func);
} }
#endif
void print(std::ostream& out, xml::document_t& document) const { void print(std::ostream& out, scope_t& scope) const {
print(out, document, true, NULL, NULL, NULL); op_t::print_context_t context(scope);
print(out, context);
} }
void dump(std::ostream& out) const { void dump(std::ostream& out) const {
if (ptr) if (ptr)
ptr->dump(out, 0); ptr->dump(out, 0);
} }
friend class scope_t;
}; };
inline xpath_t::ptr_op_t
xpath_t::op_t::new_node(kind_t _kind, ptr_op_t _left, ptr_op_t _right) {
ptr_op_t node(new op_t(_kind));
if (_left)
node->set_left(_left);
if (_right)
node->set_right(_right);
return node;
}
inline xpath_t::ptr_op_t
xpath_t::op_t::wrap_value(const value_t& val) {
xpath_t::ptr_op_t temp(new xpath_t::op_t(xpath_t::op_t::VALUE));
temp->set_value(val);
return temp;
}
inline xpath_t::ptr_op_t
xpath_t::op_t::wrap_functor(const function_t& fobj) {
xpath_t::ptr_op_t temp(new xpath_t::op_t(xpath_t::op_t::FUNCTION));
temp->set_function(fobj);
return temp;
}
template<>
inline xpath_t::symbol_scope_t&
xpath_t::scope_t::find_scope<xpath_t::symbol_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(SYMBOL_SCOPE, skip_this);
assert(scope);
return downcast<symbol_scope_t>(*scope);
}
template<>
inline xpath_t::call_scope_t&
xpath_t::scope_t::find_scope<xpath_t::call_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(CALL_SCOPE, skip_this);
assert(scope);
return downcast<call_scope_t>(*scope);
}
template<>
inline xpath_t::context_scope_t&
xpath_t::scope_t::find_scope<xpath_t::context_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(CONTEXT_SCOPE, skip_this);
assert(scope);
return downcast<context_scope_t>(*scope);
}
template<>
inline xpath_t::node_scope_t&
xpath_t::scope_t::find_scope<xpath_t::node_scope_t>(bool skip_this) {
optional<scope_t&> scope = find_scope(NODE_SCOPE, skip_this);
assert(scope);
return downcast<node_scope_t>(*scope);
}
#define FIND_SCOPE(scope_type, scope_ref) \
downcast<xml::xpath_t::scope_t>(scope_ref).find_scope<scope_type>()
} // namespace xml } // namespace xml
template <typename T> value_t xml_command(xml::xpath_t::call_scope_t& args);
inline T * get_ptr(xml::xpath_t::scope_t& locals, unsigned int idx) {
assert(locals.args.size() > idx);
T * ptr = locals.args[idx].as_pointer<T>();
assert(ptr);
return ptr;
}
template <typename T>
inline T * get_node_ptr(xml::xpath_t::scope_t& locals, unsigned int idx) {
assert(locals.args.size() > idx);
T * ptr = polymorphic_downcast<T *>(locals.args[idx].as_xml_node_mutable());
assert(ptr);
return ptr;
}
class xml_command
{
public:
value_t operator()(xml::xpath_t::scope_t& locals) {
std::ostream * out = get_ptr<std::ostream>(locals, 0);
xml::document_t * doc = get_node_ptr<xml::document_t>(locals, 1);
doc->print(*out);
return true;
}
};
} // namespace ledger } // namespace ledger