summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commodity.cc4
-rw-r--r--src/main.cc35
-rw-r--r--src/node.cc9
-rw-r--r--src/node.h5
-rw-r--r--src/option.cc6
-rw-r--r--src/pyinterp.cc20
-rw-r--r--src/pyinterp.h21
-rw-r--r--src/report.cc49
-rw-r--r--src/report.h64
-rw-r--r--src/session.cc54
-rw-r--r--src/session.h62
-rw-r--r--src/transform.h30
-rw-r--r--src/utils.h5
-rw-r--r--src/value.h134
-rw-r--r--src/xpath.cc1136
-rw-r--r--src/xpath.h643
16 files changed, 985 insertions, 1292 deletions
diff --git a/src/commodity.cc b/src/commodity.cc
index 48609577..8ab518ee 100644
--- a/src/commodity.cc
+++ b/src/commodity.cc
@@ -126,13 +126,13 @@ commodity_t::operator bool() const
annotated_commodity_t& commodity_t::as_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
{
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)
diff --git a/src/main.cc b/src/main.cc
index 522d1c9a..bca16cb4 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -136,7 +136,7 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
else
#endif
if (verb == "xml")
- command = xml_command();
+ command = bind(xml_command, _1);
else if (verb == "expr")
;
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::document_t temp(xml::LEDGER_NODE);
+ xml::xpath_t::document_scope_t doc_scope(report, temp);
+
IF_INFO() {
std::cout << "Value expression tree:" << std::endl;
expr.dump(std::cout);
std::cout << 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 << "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;
}
@@ -242,8 +244,12 @@ static int read_and_report(ledger::report_t& report, int argc, char * argv[],
}
#endif
+ report.define("ostream", value_t(out));
+
// Are we handling the expr commands? Do so now.
+ xml::xpath_t::document_scope_t doc_scope(report, xml_document);
+
if (verb == "expr") {
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);
*out << 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 << "Result of calculation: ";
}
- *out << expr.calc(xml_document, report).strip_annotations() << std::endl;
+ *out << expr.calc(doc_scope).strip_annotations() << std::endl;
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;
xml::xpath_t xpath(*arg);
- xpath.print(*out, xml_document);
+ xpath.print(*out, doc_scope);
*out << std::endl;
+#if 0
foreach (const value_t& value, xpath.find_all(xml_document, report)) {
if (value.is_xml_node()) {
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;
}
+#endif
return 0;
}
// Apply transforms to the hierarchical document structure
INFO_START(transforms, "Applied transforms");
- report.apply_transforms(xml_document);
+ report.apply_transforms(doc_scope);
INFO_FINISH(transforms);
// Create an argument scope containing the report command's
// arguments, and then invoke the command.
- xml::xpath_t::scope_t locals(report, xml::xpath_t::scope_t::ARGUMENT);
-
- locals.args.push_back(out);
- locals.args.push_back(&xml_document);
+ xml::xpath_t::call_scope_t command_args(doc_scope);
- value_t::sequence_t args_list;
- foreach (string& i, args)
- args_list.push_back(value_t(i, true));
- locals.args.push_back(args_list);
+ for (strings_list::iterator i = arg; i != args.end(); i++)
+ command_args.push_back(value_t(*i, true));
INFO_START(command, "Did user command '" << verb << "'");
- command(locals);
+ command(command_args);
INFO_FINISH(command);
diff --git a/src/node.cc b/src/node.cc
index f19b5989..55b388e0 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -40,6 +40,15 @@ const char * node_t::name() const
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)
{
for (const char * s = str.c_str(); *s; s++) {
diff --git a/src/node.h b/src/node.h
index 2a137e86..7b1f9919 100644
--- a/src/node.h
+++ b/src/node.h
@@ -87,12 +87,12 @@ public:
parent_node_t& as_parent_node() {
if (! is_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 {
if (! is_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;
@@ -116,6 +116,7 @@ public:
attributes = attributes_t();
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 {
if (attributes) {
typedef attributes_t::nth_index<1>::type attributes_by_name;
diff --git a/src/option.cc b/src/option.cc
index bf0bdf55..0b7f7ef9 100644
--- a/src/option.cc
+++ b/src/option.cc
@@ -82,11 +82,11 @@ namespace {
#if 0
try {
#endif
- xml::xpath_t::scope_t arguments(scope, xml::xpath_t::scope_t::ARGUMENT);
+ xml::xpath_t::call_scope_t args(scope);
if (arg)
- arguments.args.push_back(value_t(arg, true));
+ args.push_back(value_t(arg, true));
- opt(arguments);
+ opt(args);
#if 0
}
catch (error * err) {
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index 861f822a..d521a0ee 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -87,7 +87,7 @@ struct python_run
};
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__"))),
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();
}
-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 {
if (! PyCallable_Check(func.ptr())) {
return extract<value_t>(func.ptr());
} else {
- if (locals.args.size() > 0) {
+ if (args.size() > 0) {
list arglist;
- foreach (const value_t& value, locals.args)
- arglist.append(value);
+ if (args.value().is_sequence())
+ foreach (const value_t& value, args.value().as_sequence())
+ arglist.append(value);
+ else
+ arglist.append(args.value());
if (PyObject * val =
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()
- (xml::xpath_t::scope_t& locals)
+ (xml::xpath_t::call_scope_t& args)
{
try {
- assert(locals.args.size() == 1);
- value_t item = locals.args[0];
+ assert(args.size() == 1);
+ value_t item = args[0];
assert(item.is_xml_node());
return call<value_t>(func.ptr(), item.as_xml_node());
}
diff --git a/src/pyinterp.h b/src/pyinterp.h
index 037c70e1..3d69d972 100644
--- a/src/pyinterp.h
+++ b/src/pyinterp.h
@@ -29,8 +29,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _PY_EVAL_H
-#define _PY_EVAL_H
+#ifndef _PYINTERP_H
+#define _PYINTERP_H
#include "xpath.h"
@@ -39,7 +39,7 @@
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;
@@ -76,28 +76,23 @@ class python_interpreter_t : public xml::xpath_t::scope_t
public:
functor_t(const string& name, boost::python::object _func) : func(_func) {}
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) {
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
- return parent ? parent->lookup(name) : NULL;
+ return xml::xpath_t::symbol_scope_t::lookup(name);
}
class lambda_t : public functor_t {
public:
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
-#endif // _PY_EVAL_H
+#endif // _PYINTERP_H
diff --git a/src/report.cc b/src/report.cc
index 2702d12e..ee3382da 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -38,41 +38,46 @@ report_t::~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)
- transform.execute(document);
+ typedef tuple<shared_ptr<transform_t>, value_t> transform_details_tuple;
+
+ 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])");
- string str = locals.args[0].as_string();
- long wid = locals.args[1];
+ string str = args[0].as_string();
+ long wid = args[1];
elision_style_t style = session.elision_style;
- if (locals.args.size() == 3)
- style = (elision_style_t)locals.args[2].as_long();
+ if (args.size() == 3)
+ style = (elision_style_t)args[2].as_long();
long abbrev_len = session.abbrev_length;
- if (locals.args.size() == 4)
- abbrev_len = locals.args[3].as_long();
+ if (args.size() == 4)
+ abbrev_len = args[3].as_long();
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])");
- moment_t date = locals.args[0].as_datetime();
+ moment_t date = args[0].as_datetime();
string date_format;
- if (locals.args.size() == 2)
- date_format = locals.args[1].as_string();
+ if (args.size() == 2)
+ date_format = args[1].as_string();
#if 0
// jww (2007-04-18): Need to setup an output facet here
else
@@ -84,25 +89,27 @@ value_t report_t::ftime(xml::xpath_t::scope_t& locals)
#endif
}
+#if 0
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();
switch (*p) {
case 'a':
if (name == "abbrev") {
- return abbrev(locals);
+ return abbrev(args);
}
break;
case 'f':
if (name == "ftime") {
- return ftime(locals);
+ return ftime(args);
}
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)
{
@@ -210,7 +217,7 @@ xml::xpath_t::ptr_op_t report_t::lookup(const string& name)
break;
}
- return xml::xpath_t::scope_t::lookup(name);
+ return xml::xpath_t::symbol_scope_t::lookup(name);
}
} // namespace ledger
diff --git a/src/report.h b/src/report.h
index 45c39c60..b435341b 100644
--- a/src/report.h
+++ b/src/report.h
@@ -39,9 +39,9 @@ namespace ledger {
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;
string format_string;
string amount_expr;
@@ -59,10 +59,10 @@ class report_t : public xml::xpath_t::scope_t
session_t& session;
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)
- : xml::xpath_t::scope_t(_session),
+ : xml::xpath_t::symbol_scope_t(downcast<xml::xpath_t::scope_t>(_session)),
show_totals(false),
raw_mode(false),
session(_session),
@@ -76,14 +76,14 @@ class report_t : public xml::xpath_t::scope_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
//
- value_t ftime(xml::xpath_t::scope_t& locals);
- value_t abbrev(xml::xpath_t::scope_t& locals);
+ value_t ftime(xml::xpath_t::call_scope_t& args);
+ value_t abbrev(xml::xpath_t::call_scope_t& args);
//
// 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);
#endif
}
- value_t option_eval(xml::xpath_t::scope_t& locals) {
- eval(locals.args[0].as_string());
+ value_t option_eval(xml::xpath_t::call_scope_t& args) {
+ eval(args[0].as_string());
return NULL_VALUE;
}
- value_t option_amount(xml::xpath_t::scope_t& locals) {
- eval(string("t=") + locals.args[0].as_string());
+ value_t option_amount(xml::xpath_t::call_scope_t& args) {
+ eval(string("t=") + args[0].as_string());
return NULL_VALUE;
}
- value_t option_total(xml::xpath_t::scope_t& locals) {
- eval(string("T()=") + locals.args[0].as_string());
+ value_t option_total(xml::xpath_t::call_scope_t& args) {
+ eval(string("T()=") + args[0].as_string());
return NULL_VALUE;
}
- value_t option_format(xml::xpath_t::scope_t& locals) {
- format_string = locals.args[0].as_string();
+ value_t option_format(xml::xpath_t::call_scope_t& args) {
+ format_string = args[0].as_string();
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;
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;
return NULL_VALUE;
}
- value_t option_bar(xml::xpath_t::scope_t& locals) {
- std::cout << "This is bar: " << locals.args[0] << std::endl;
+ value_t option_bar(xml::xpath_t::call_scope_t& args) {
+ std::cout << "This is bar: " << args[0] << std::endl;
return NULL_VALUE;
}
@@ -132,44 +132,44 @@ class report_t : public xml::xpath_t::scope_t
//
#if 0
- value_t option_select(xml::xpath_t::scope_t& locals) {
- transforms.push_back(new select_transform(locals.args[0].as_string()));
+ value_t option_select(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new select_transform(args[0].as_string()));
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[") +
- locals.args[0].as_string() + "]");
+ args[0].as_string() + "]");
transforms.push_back(new select_transform(expr));
return NULL_VALUE;
}
- value_t option_remove(xml::xpath_t::scope_t& locals) {
- transforms.push_back(new remove_transform(locals.args[0].as_string()));
+ value_t option_remove(xml::xpath_t::call_scope_t& args) {
+ transforms.push_back(new remove_transform(args[0].as_string()));
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);
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);
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);
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);
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);
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);
return NULL_VALUE;
}
@@ -179,8 +179,6 @@ class report_t : public xml::xpath_t::scope_t
// 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);
};
diff --git a/src/session.cc b/src/session.cc
index c886dde5..71f5f349 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -68,6 +68,56 @@ void release_session_context()
#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,
const path& pathname,
xml::builder_t& builder)
@@ -173,6 +223,7 @@ std::size_t session_t::read_data(xml::builder_t& builder,
return entry_count;
}
+#if 0
optional<value_t>
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);
}
+#endif
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;
}
- 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
diff --git a/src/session.h b/src/session.h
index 5c3776bb..206144c6 100644
--- a/src/session.h
+++ b/src/session.h
@@ -39,7 +39,7 @@
namespace ledger {
-class session_t : public xml::xpath_t::scope_t
+class session_t : public xml::xpath_t::symbol_scope_t
{
public:
static session_t * current;
@@ -79,53 +79,7 @@ class session_t : public xml::xpath_t::scope_t
ptr_list<journal_t> journals;
ptr_list<parser_t> parsers;
- 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&");
- }
-
+ session_t();
virtual ~session_t() {
TRACE_DTOR(session_t);
}
@@ -178,8 +132,6 @@ class session_t : public xml::xpath_t::scope_t
// 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);
//
@@ -208,19 +160,19 @@ class session_t : public xml::xpath_t::scope_t
// Option handlers
//
- value_t option_file_(xml::xpath_t::scope_t& locals) {
- assert(locals.args.size() == 1);
- data_file = locals.args[0].as_string();
+ value_t option_file_(xml::xpath_t::call_scope_t& args) {
+ assert(args.size() == 1);
+ data_file = args[0].as_string();
return NULL_VALUE;
}
#if 0
#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);
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);
return NULL_VALUE;
}
diff --git a/src/transform.h b/src/transform.h
index 5d6b1976..158b9b6a 100644
--- a/src/transform.h
+++ b/src/transform.h
@@ -39,38 +39,38 @@ namespace ledger {
class transform_t {
public:
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 {
// --check checks the validity of the item list.
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 {
// --accounts transforms the report tree into an account-wise view.
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 {
// --compact compacts an account tree to remove accounts with only
// one child account.
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 {
// --clean clears out entries and accounts that have no contents.
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 {
// --entries transforms the report tree into an entries-wise view.
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 {
@@ -79,7 +79,7 @@ class optimize_transform : public transform_t {
// commodity (one the negative of the other), the amount of the
// second transaction will be nulled out.
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 {
@@ -89,7 +89,7 @@ class split_transform : public transform_t {
// useful before sorting, for exampel, in order to sort by
// transaction instead of by entry.
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 {
@@ -97,7 +97,7 @@ class merge_transform : public transform_t {
// which share the same entry will be merged into a group of
// transactions under one reported entry.
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 {
@@ -107,14 +107,14 @@ class combine_transform : public transform_t {
// will show the terminating date or a label that is characteristic
// of the set).
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 {
// --group groups all transactions that affect the same account
// within an entry, so that they appear as a single transaction.
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 {
@@ -123,7 +123,7 @@ class collapse_transform : public transform_t {
// fictitous account "<total>" is used to represent the final sum,
// if multiple accounts are involved.
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 {
@@ -131,7 +131,7 @@ class subtotal_transform : public transform_t {
// one giant entry. When used in conjunction with --group, the
// affect is very similar to a regular balance report.
public:
- virtual void execute(xml::document_t& document);
+ virtual value_t operator()(xml::xpath_t::call_scope_t& args);
};
#if 0
@@ -146,7 +146,7 @@ class select_transform : public transform_t
}
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
@@ -155,7 +155,7 @@ class remove_transform : public select_transform
remove_transform(const string& 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
diff --git a/src/utils.h b/src/utils.h
index 7bc90fb6..cf1cd1d0 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -521,6 +521,11 @@ inline void throw_unexpected_error(char, char) {
namespace ledger {
+template <typename T, typename U>
+inline T& downcast(U& object) {
+ return *polymorphic_downcast<T *>(&object);
+}
+
path resolve_path(const path& pathname);
#ifdef HAVE_REALPATH
diff --git a/src/value.h b/src/value.h
index 2371b74e..c9281121 100644
--- a/src/value.h
+++ b/src/value.h
@@ -61,6 +61,10 @@ class value_t
public:
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 {
VOID,
BOOLEAN,
@@ -72,7 +76,6 @@ public:
STRING,
SEQUENCE,
XML_NODE,
- CONST_XML_NODE,
POINTER
};
@@ -217,12 +220,9 @@ public:
TRACE_CTOR(value_t, "xml::node_t *");
set_xml_node(xml_node);
}
- value_t(const xml::node_t * xml_node) {
- TRACE_CTOR(value_t, "const xml::node_t *");
- set_xml_node(xml_node);
- }
- value_t(void * item) {
- TRACE_CTOR(value_t, "void *");
+ template <typename T>
+ value_t(T * item) {
+ TRACE_CTOR(value_t, "T *");
set_pointer(item);
}
~value_t() {
@@ -254,11 +254,22 @@ public:
else
storage->destroy();
}
+ void _reset() {
+ if (storage) {
+ storage->destroy();
+ storage = intrusive_ptr<storage_t>();
+ }
+ }
operator bool() 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 result = storage ? storage->type : VOID;
@@ -272,9 +283,14 @@ private:
}
void set_type(type_t new_type) {
assert(new_type >= VOID && new_type <= POINTER);
- _clear();
- storage->type = new_type;
- assert(is_type(new_type));
+ if (new_type == VOID) {
+ _reset();
+ assert(is_null());
+ } else {
+ _clear();
+ storage->type = new_type;
+ assert(is_type(new_type));
+ }
}
public:
@@ -415,36 +431,21 @@ public:
}
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() {
assert(is_xml_node());
- assert(! is_type(CONST_XML_NODE));
_dup();
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_type(CONST_XML_NODE));
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) {
set_type(XML_NODE);
*(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 {
return is_type(POINTER);
@@ -460,6 +461,12 @@ public:
_dup();
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 {
assert(is_pointer());
return *(boost::any *) storage->data;
@@ -469,6 +476,11 @@ public:
assert(is_pointer());
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) {
set_type(POINTER);
new((boost::any *) storage->data) boost::any(val);
@@ -496,18 +508,66 @@ public:
void in_place_simplify();
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 {
- 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) {
- 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 {
- 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);
@@ -542,7 +602,6 @@ public:
case SEQUENCE:
return "a sequence";
case XML_NODE:
- case CONST_XML_NODE:
return "an xml node";
case POINTER:
return "a pointer";
@@ -602,19 +661,12 @@ public:
friend std::ostream& operator<<(std::ostream& out, const value_t& val);
};
-template <>
-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;
-}
+#define NULL_VALUE (value_t())
std::ostream& operator<<(std::ostream& out, const value_t& val);
DECLARE_EXCEPTION(value_error);
-#define NULL_VALUE (value_t())
-
} // namespace ledger
#endif // _VALUE_H
diff --git a/src/xpath.cc b/src/xpath.cc
index 8a05d853..5ff5555d 100644
--- a/src/xpath.cc
+++ b/src/xpath.cc
@@ -168,12 +168,10 @@ void xpath_t::token_t::next(std::istream& in, flags_t flags)
in.get(c);
kind = AT_SYM;
break;
-#if 0
case '$':
in.get(c);
kind = DOLLAR;
break;
-#endif
case '(':
in.get(c);
@@ -208,12 +206,14 @@ void xpath_t::token_t::next(std::istream& in, flags_t flags)
break;
}
+ case '\'':
case '"': {
- in.get(c);
+ char delim;
+ in.get(delim);
char buf[4096];
- READ_INTO_(in, buf, 4095, c, length, c != '"');
- if (c != '"')
- unexpected(c, '"');
+ READ_INTO_(in, buf, 4095, c, length, c != delim);
+ if (c != delim)
+ unexpected(c, delim);
in.get(c);
length++;
kind = VALUE;
@@ -259,14 +259,6 @@ void xpath_t::token_t::next(std::istream& in, flags_t flags)
case '*':
in.get(c);
- if (in.peek() == '*') {
- in.get(c);
- symbol[1] = c;
- symbol[2] = '\0';
- kind = POWER;
- length = 2;
- break;
- }
kind = STAR;
break;
@@ -306,30 +298,10 @@ void xpath_t::token_t::next(std::istream& in, flags_t flags)
kind = GREATER;
break;
- case '&':
- in.get(c);
- kind = AMPER;
- break;
case '|':
in.get(c);
kind = PIPE;
break;
- case '?':
- in.get(c);
- kind = QUESTION;
- break;
- case ':':
- in.get(c);
- if (in.peek() == '=') {
- in.get(c);
- symbol[1] = c;
- symbol[2] = '\0';
- kind = ASSIGN;
- length = 2;
- break;
- }
- kind = COLON;
- break;
case ',':
in.get(c);
kind = COMMA;
@@ -433,21 +405,7 @@ void xpath_t::token_t::unexpected(char c, char wanted)
}
}
-xpath_t::ptr_op_t xpath_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;
-}
-
-xpath_t::ptr_op_t xpath_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;
-}
-
-void xpath_t::scope_t::define(const string& name, ptr_op_t def)
+void xpath_t::symbol_scope_t::define(const string& name, ptr_op_t def)
{
DEBUG("ledger.xpath.syms", "Defining '" << name << "' = " << def);
@@ -468,44 +426,56 @@ void xpath_t::scope_t::define(const string& name, ptr_op_t def)
def->acquire();
}
-xpath_t::ptr_op_t
-xpath_t::scope_t::lookup(const string& name)
+void xpath_t::scope_t::define(const string& name, const value_t& val) {
+ define(name, op_t::wrap_value(val));
+}
+
+value_t xpath_fn_last(xpath_t::call_scope_t& scope)
{
- symbol_map::const_iterator i = symbols.find(name);
- if (i != symbols.end())
- return (*i).second;
- else if (parent)
- return parent->lookup(name);
- return NULL;
+ xpath_t::context_scope_t& context(FIND_SCOPE(xpath_t::context_scope_t, scope));
+
+ return context.size();
}
-void xpath_t::scope_t::define(const string& name, const function_t& def) {
- define(name, wrap_functor(def));
+value_t xpath_fn_position(xpath_t::call_scope_t& scope)
+{
+ xpath_t::context_scope_t& context(FIND_SCOPE(xpath_t::context_scope_t, scope));
+
+ return context.index();
}
-optional<value_t>
-xpath_t::function_scope_t::resolve(const string& name, scope_t& locals)
+value_t xpath_fn_text(xpath_t::call_scope_t& scope)
+{
+ xpath_t::context_scope_t& context(FIND_SCOPE(xpath_t::context_scope_t, scope));
+
+ return value_t(context.xml_node().to_value().to_string(), true);
+}
+
+xpath_t::ptr_op_t
+xpath_t::symbol_scope_t::lookup(const string& name)
{
switch (name[0]) {
case 'l':
- if (name == "last") {
- return value_t((long)size);
- }
+ if (name == "last")
+ return WRAP_FUNCTOR(bind(xpath_fn_last, _1));
break;
case 'p':
- if (name == "position") {
- return value_t((long)index + 1);
- }
+ if (name == "position")
+ return WRAP_FUNCTOR(bind(xpath_fn_position, _1));
break;
case 't':
- if (name == "text") {
- return node.to_value();
- }
+ if (name == "text")
+ return WRAP_FUNCTOR(bind(xpath_fn_text, _1));
break;
}
- return scope_t::resolve(name, locals);
+
+ symbol_map::const_iterator i = symbols.find(name);
+ if (i != symbols.end())
+ return (*i).second;
+
+ return child_scope_t::lookup(name);
}
xpath_t::ptr_op_t
@@ -557,7 +527,7 @@ xpath_t::parse_value_term(std::istream& in, flags_t tflags) const
node = new op_t(op_t::FUNC_NAME);
node->set_string(ident);
- ptr_op_t call_node(new op_t(op_t::O_EVAL));
+ ptr_op_t call_node(new op_t(op_t::O_CALL));
call_node->set_left(node);
call_node->set_right(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL));
@@ -602,16 +572,14 @@ xpath_t::parse_value_term(std::istream& in, flags_t tflags) const
break;
}
-#if 0
case token_t::DOLLAR:
tok = next_token(in, tflags);
if (tok.kind != token_t::IDENT)
throw parse_error("$ symbol must be followed by variable name");
node = new op_t(op_t::VAR_NAME);
- node->name = new string(tok.value.as_string());
+ node->set_string(tok.value.as_string());
break;
-#endif
case token_t::DOT:
node = new op_t(op_t::NODE_ID);
@@ -632,10 +600,11 @@ xpath_t::parse_value_term(std::istream& in, flags_t tflags) const
break;
case token_t::LPAREN:
- node = parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL);
- if (! node)
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
+ node = new op_t(op_t::O_COMMA);
+ node->set_left(parse_value_expr(in, tflags | XPATH_PARSE_PARTIAL));
+ if (! node->left())
+ throw_(parse_error, tok.symbol << " operator not followed by argument");
+
tok = next_token(in, tflags);
if (tok.kind != token_t::RPAREN)
tok.unexpected(); // jww (2006-09-09): wanted )
@@ -845,9 +814,6 @@ xpath_t::parse_logic_expr(std::istream& in, flags_t tflags) const
flags_t _flags = tflags;
token_t& tok = next_token(in, tflags);
switch (tok.kind) {
- case token_t::ASSIGN:
- kind = op_t::O_DEFINE;
- break;
case token_t::EQUAL:
kind = op_t::O_EQ;
break;
@@ -875,10 +841,7 @@ xpath_t::parse_logic_expr(std::istream& in, flags_t tflags) const
ptr_op_t prev(node);
node = new op_t(kind);
node->set_left(prev);
- if (kind == op_t::O_DEFINE)
- node->set_right(parse_querycolon_expr(in, tflags));
- else
- node->set_right(parse_add_expr(in, _flags));
+ node->set_right(parse_add_expr(in, _flags));
if (! node->right()) {
if (tok.kind == token_t::PLUS)
@@ -939,39 +902,9 @@ xpath_t::parse_or_expr(std::istream& in, flags_t tflags) const
}
xpath_t::ptr_op_t
-xpath_t::parse_querycolon_expr(std::istream& in, flags_t tflags) const
-{
- ptr_op_t node(parse_or_expr(in, tflags));
-
- if (node) {
- token_t& tok = next_token(in, tflags);
- if (tok.kind == token_t::QUESTION) {
- ptr_op_t prev(node);
- node = new op_t(op_t::O_QUES);
- node->set_left(prev);
- node->set_right(new op_t(op_t::O_COLON));
- node->right()->set_left(parse_querycolon_expr(in, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
- tok = next_token(in, tflags);
- if (tok.kind != token_t::COLON)
- tok.unexpected(); // jww (2006-09-09): wanted :
- node->right()->set_right(parse_querycolon_expr(in, tflags));
- if (! node->right())
- throw_(parse_error,
- tok.symbol << " operator not followed by argument");
- } else {
- push_token(tok);
- }
- }
- return node;
-}
-
-xpath_t::ptr_op_t
xpath_t::parse_value_expr(std::istream& in, flags_t tflags) const
{
- ptr_op_t node(parse_querycolon_expr(in, tflags));
+ ptr_op_t node(parse_or_expr(in, tflags));
if (node) {
token_t& tok = next_token(in, tflags);
@@ -1022,571 +955,275 @@ xpath_t::parse_expr(std::istream& in, flags_t tflags) const
return node;
}
-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;
-}
-
-xpath_t::ptr_op_t
-xpath_t::op_t::copy(ptr_op_t tleft, ptr_op_t tright) const
-{
- ptr_op_t node(new op_t(kind));
- if (tleft)
- node->set_left(tleft);
- if (tright)
- node->set_right(tright);
- return node;
-}
-
-xpath_t::ptr_op_t xpath_t::op_t::defer_sequence(value_t::sequence_t& result_seq)
+xpath_t::ptr_op_t xpath_t::op_t::compile(scope_t& scope)
{
- // If not all of the elements were constants, transform the result
- // into an expression sequence using O_COMMA.
-
- assert(! result_seq.empty());
+ switch (kind) {
+ case VAR_NAME:
+ case FUNC_NAME:
+ if (ptr_op_t def = scope.lookup(as_string()))
+ // jww (2007-05-16): Aren't definitions compiled when they go
+ // in? Does recompiling here really stand a chance of adding
+ // any benefit?
+ return def->compile(scope);
+ return this;
- if (result_seq.size() == 1)
- return wrap_value(result_seq.front());
+ default:
+ break;
+ }
- value_t::sequence_t::iterator i = result_seq.begin();
+ ptr_op_t lhs(left()->compile(scope));
+ ptr_op_t rhs(right() ? right()->compile(scope) : ptr_op_t());
- ptr_op_t lit_seq(new op_t(O_COMMA));
+ if (lhs == left() && (! rhs || rhs == right()))
+ return this;
- lit_seq->set_left(wrap_value(*i++));
- ptr_op_t* opp = &lit_seq->right();
+ ptr_op_t intermediate(copy(lhs, rhs));
- for (; i != result_seq.end(); i++) {
- if (*opp) {
- ptr_op_t val = *opp;
- *opp = new op_t(O_COMMA);
- (*opp)->set_left(val);
- opp = &(*opp)->right();
- }
+ if (lhs->is_value() && (! rhs || rhs->is_value()))
+ return wrap_value(intermediate->calc(scope));
- if (! (*i).is_pointer())
- *opp = wrap_value(*i);
- else
-#if 1
- assert(false);
-#else
- *opp = static_cast<ptr_op_t>((*i).as_pointer());
-#endif
- }
+ return intermediate;
+}
- return lit_seq;
+value_t xpath_t::op_t::current_value(scope_t& scope)
+{
+ xpath_t::context_scope_t& context(FIND_SCOPE(xpath_t::context_scope_t, scope));
+ return context.value();
}
-void xpath_t::op_t::append_value(value_t::sequence_t& result_seq, value_t& val)
+node_t& xpath_t::op_t::current_xml_node(scope_t& scope)
{
- if (val.is_sequence())
- std::for_each(val.as_sequence().begin(), val.as_sequence().end(),
- bind(&value_t::sequence_t::push_back, ref(result_seq), _1));
- else
- result_seq.push_back(val);
+ xpath_t::context_scope_t& context(FIND_SCOPE(xpath_t::context_scope_t, scope));
+ return context.xml_node();
}
-xpath_t::ptr_op_t
-xpath_t::op_t::compile(const node_t& context, scope_t& scope, bool resolve)
+value_t xpath_t::op_t::calc(scope_t& scope)
{
-#if 0
- try {
-#endif
switch (kind) {
case VALUE:
- return this;
-
- case ATTR_ID:
- if (optional<const string&> value = context.get_attr(as_long()))
- return wrap_value(value_t(*value, true));
- return this;
-
- case ATTR_NAME:
- if (optional<node_t::nameid_t> id =
- context.document().lookup_name_id(as_string())) {
- if (optional<const string&> value = context.get_attr(*id))
- return wrap_value(value_t(*value, true));
- }
- return this;
+ return as_value();
case VAR_NAME:
case FUNC_NAME:
- if (resolve) {
- scope_t null_scope;
- if (optional<value_t> temp = scope.resolve(as_string(), null_scope))
- return wrap_value(*temp);
- }
- if (ptr_op_t def = scope.lookup(as_string()))
- return def->compile(context, scope, resolve);
- return this;
-
- case ARG_INDEX:
- if (scope.kind == scope_t::ARGUMENT) {
- if (as_long() < scope.args.size())
- return wrap_value(scope.args[as_long()]);
- else
- throw_(compile_error, "Reference to non-existing argument");
- } else {
- return this;
- }
+ if (ptr_op_t reference = compile(scope))
+ return reference->calc(scope);
+ else
+ throw_(calc_error, "Failed to lookup variable or function named '"
+ << as_string() << "'");
+ break;
case FUNCTION:
- if (resolve)
- return wrap_value(as_function()(scope));
- else
- return this;
+ // This should never be evaluated directly, but should only appear
+ // as the left node of an O_CALL operator.
+ assert(false);
break;
- case O_NOT: {
- xpath_t expr(left()->compile(context, scope, resolve));
- if (! expr.ptr->is_value()) {
- if (left() == expr.ptr)
- return this;
- else
- return copy(expr.ptr);
- }
+ case O_CALL: {
+ call_scope_t call_args(scope);
- if (left() == expr.ptr) {
- if (expr.ptr->as_value().strip_annotations())
- return wrap_value(false);
- else
- return wrap_value(true);
- } else {
- if (expr.ptr->as_value().strip_annotations())
- expr.ptr->set_value(false);
- else
- expr.ptr->set_value(true);
+ call_args.set_args(right()->calc(scope));
- return expr.ptr;
- }
- }
+ ptr_op_t func = left();
+ string name;
- case O_NEG: {
- xpath_t expr(left()->compile(context, scope, resolve));
- if (! expr.ptr->is_value()) {
- if (left() == expr.ptr)
- return this;
- else
- return copy(expr.ptr);
+ if (func->kind == FUNC_NAME) {
+ name = func->as_string();
+ func = func->compile(scope);
}
- if (left() == expr.ptr) {
- return wrap_value(expr.ptr->as_value().negate());
- } else {
- expr.ptr->as_value().in_place_negate();
- return expr.ptr;
- }
- }
-
- case O_UNION: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value() || ! rexpr.ptr->is_value()) {
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
- }
+ if (func->kind != FUNCTION)
+ throw_(calc_error,
+ name.empty() ? string("Attempt to call non-function") :
+ (string("Attempt to call unknown function '") + name + "'"));
- value_t::sequence_t result_seq;
+ return func->as_function()(call_args);
+ }
- append_value(result_seq, lexpr.ptr->as_value());
- append_value(result_seq, rexpr.ptr->as_value());
+ case ARG_INDEX: {
+ call_scope_t& args(scope.find_scope<call_scope_t>());
- return wrap_value(result_seq);
+ if (as_long() >= 0 && as_long() < args.size())
+ return args[as_long()];
+ else
+ throw_(compile_error, "Reference to a non-existing argument");
+ break;
}
- case O_ADD:
- case O_SUB:
- case O_MUL:
- case O_DIV: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value() || ! rexpr.ptr->is_value()) {
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
- }
+ case O_FIND:
+ case O_RFIND: {
+ value_t result;
- if (left() == lexpr.ptr) {
- value_t temp(lexpr.ptr->as_value());
- switch (kind) {
- case O_ADD: temp += rexpr.ptr->as_value(); break;
- case O_SUB: temp -= rexpr.ptr->as_value(); break;
- case O_MUL: temp *= rexpr.ptr->as_value(); break;
- case O_DIV: temp /= rexpr.ptr->as_value(); break;
- default: assert(false); break;
- }
- return wrap_value(temp);
- } else {
- switch (kind) {
- case O_ADD: lexpr.ptr->as_value() += rexpr.ptr->as_value(); break;
- case O_SUB: lexpr.ptr->as_value() -= rexpr.ptr->as_value(); break;
- case O_MUL: lexpr.ptr->as_value() *= rexpr.ptr->as_value(); break;
- case O_DIV: lexpr.ptr->as_value() /= rexpr.ptr->as_value(); break;
- default: assert(false); break;
+ if (value_t items = left()->calc(scope)) {
+ value_t sequence = items.to_sequence();
+ foreach (value_t& item, sequence.as_sequence_lval()) {
+ if (item.is_xml_node()) {
+ node_t& node(*item.as_xml_node());
+ node_scope_t node_scope(scope, node);
+
+ result.push_back(right()->calc(node_scope));
+#if 0
+ // jww (2007-05-17): How do I get it to recurse down? I'll
+ // have to use a recursive helper function.
+ if (kind == O_RFIND && node.is_parent_node())
+ foreach (node_t * child, node.as_parent_node())
+ walk_elements(element->right(), scope, *child, NULL, func);
+#endif
+ } else {
+ throw_(compile_error, "Application of / operator to non-node value");
+ }
}
- return lexpr.ptr;
}
+ return result;
}
- case O_NEQ:
- case O_EQ:
- case O_LT:
- case O_LTE:
- case O_GT:
- case O_GTE: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value() || ! rexpr.ptr->is_value()) {
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
+ case NODE_ID:
+ switch (as_name()) {
+ case document_t::CURRENT:
+ return current_value(scope);
+
+ case document_t::PARENT:
+ if (optional<parent_node_t&> parent = current_xml_node(scope).parent())
+ return &*parent;
else
- return copy(lexpr.ptr, rexpr.ptr);
- }
+ throw_(std::logic_error, "Attempt to access parent of root node");
+ break;
- if (left() == lexpr.ptr) {
- switch (kind) {
- case O_NEQ:
- return wrap_value(lexpr.ptr->as_value() != rexpr.ptr->as_value());
- break;
- case O_EQ:
- return wrap_value(lexpr.ptr->as_value() == rexpr.ptr->as_value());
- break;
- case O_LT:
- return wrap_value(lexpr.ptr->as_value() < rexpr.ptr->as_value());
- break;
- case O_LTE:
- return wrap_value(lexpr.ptr->as_value() <= rexpr.ptr->as_value());
- break;
- case O_GT:
- return wrap_value(lexpr.ptr->as_value() > rexpr.ptr->as_value());
- break;
- case O_GTE:
- return wrap_value(lexpr.ptr->as_value() >= rexpr.ptr->as_value());
- break;
- default: assert(false); break;
- }
- } else {
- switch (kind) {
- case O_NEQ:
- lexpr.ptr->set_value(lexpr.ptr->as_value() != rexpr.ptr->as_value());
- break;
- case O_EQ:
- lexpr.ptr->set_value(lexpr.ptr->as_value() == rexpr.ptr->as_value());
- break;
- case O_LT:
- lexpr.ptr->set_value(lexpr.ptr->as_value() < rexpr.ptr->as_value());
- break;
- case O_LTE:
- lexpr.ptr->set_value(lexpr.ptr->as_value() <= rexpr.ptr->as_value());
- break;
- case O_GT:
- lexpr.ptr->set_value(lexpr.ptr->as_value() > rexpr.ptr->as_value());
- break;
- case O_GTE:
- lexpr.ptr->set_value(lexpr.ptr->as_value() >= rexpr.ptr->as_value());
- break;
- default:
- assert(false);
- break;
- }
- return lexpr.ptr;
- }
- }
+ case document_t::ROOT:
+ return &current_xml_node(scope).document();
- case O_AND: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- if (lexpr.ptr->is_value() && ! lexpr.ptr->as_value().strip_annotations()) {
- lexpr.ptr->set_value(false);
- return lexpr.ptr;
- }
+ case document_t::ALL: {
+ node_t& current_node(current_xml_node(scope));
+ if (! current_node.is_parent_node())
+ throw_(compile_error, "Referencing child nodes from a non-parent value");
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value() || ! rexpr.ptr->is_value()) {
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
+ value_t result;
+ foreach (node_t * child, current_node.as_parent_node())
+ result.push_back(child);
+ return result;
}
- if (! rexpr.ptr->as_value().strip_annotations()) {
- if (left() == lexpr.ptr) {
- return wrap_value(false);
- } else {
- lexpr.ptr->set_value(false);
- return lexpr.ptr;
- }
- } else {
- return rexpr.ptr;
+ default:
+ break; // pass down to the NODE_NAME case
}
- }
+ // fall through...
- case O_OR: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- if (lexpr.ptr->is_value() && lexpr.ptr->as_value().strip_annotations())
- return lexpr.ptr;
+ case NODE_NAME: {
+ node_t& current_node(current_xml_node(scope));
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value() || ! rexpr.ptr->is_value()) {
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
- }
+ if (current_node.is_parent_node()) {
+ bool have_name_id = kind == NODE_ID;
- if (rexpr.ptr->as_value().strip_annotations()) {
- return rexpr.ptr;
- } else {
- if (left() == lexpr.ptr) {
- return wrap_value(false);
- } else {
- lexpr.ptr->set_value(false);
- return lexpr.ptr;
+ value_t result;
+
+ foreach (node_t * child, current_node.as_parent_node()) {
+ if (( have_name_id && as_name() == child->name_id()) ||
+ (! have_name_id && as_string() == child->name()))
+ result.push_back(child);
+#if 0
+ else if (recurse)
+ /* ... */;
+#endif
}
+ return result;
}
+ return NULL_VALUE;
}
- case O_QUES: {
- assert(right()->kind == O_COLON);
- xpath_t lexpr(left()->compile(context, scope, resolve));
- if (! lexpr.ptr->is_value()) {
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
- }
-
- if (lexpr.ptr->as_value().strip_annotations())
- return right()->left()->compile(context, scope, resolve);
- else
- return right()->right()->compile(context, scope, resolve);
+ case O_PRED: {
+#if 0
+ predicate_scope_t predicate_scope(scope, right());
+ return left()->calc(predicate_scope);
+#endif
+ break;
}
- case O_COLON: {
- xpath_t lexpr(left()->compile(context, scope, resolve));
- xpath_t rexpr(right()->compile(context, scope, resolve));
- if (left() == lexpr.ptr && right() == rexpr.ptr)
- return this;
- else
- return copy(lexpr.ptr, rexpr.ptr);
- }
+ case ATTR_ID:
+ case ATTR_NAME: {
+ context_scope_t& context(scope.find_scope<context_scope_t>());
- case O_COMMA: {
- // jww (2006-09-29): This should act just like union
- xpath_t lexpr(left()->compile(context, scope, resolve)); // for side-effects
- return right()->compile(context, scope, resolve);
- }
+ if (context.type() != scope_t::NODE_SCOPE)
+ throw_(calc_error, "Looking up attribute in a non-node context");
- case O_DEFINE:
- if (left()->kind == VAR_NAME || left()->kind == FUNC_NAME) {
- xpath_t rexpr(right()->compile(context, scope, resolve));
- scope.define(left()->as_string(), rexpr.ptr);
- return rexpr.ptr;
- } else {
- assert(left()->kind == O_EVAL);
- assert(left()->left()->kind == FUNC_NAME);
+ node_scope_t& node_scope(downcast<node_scope_t>(context));
-#if 0
- // jww (2006-09-16): If I compile the definition of a function,
- // I eliminate the possibility of future lookups
-
- scope_t arg_scope(scope);
-
- unsigned int index = 0;
- ptr_op_t args = left()->right();
- while (args) {
- ptr_op_t arg = args;
- if (args->kind == O_COMMA) {
- arg = args->left();
- args = args->right();
- } else {
- args = NULL;
- }
+ if (optional<const string&> value =
+ kind == ATTR_ID ? node_scope.xml_node().get_attr(as_long()) :
+ node_scope.xml_node().get_attr(as_string()))
+ return value_t(*value, true);
+ else
+ throw_(calc_error, "Attribute '"
+ << (kind == ATTR_ID ?
+ *node_scope.xml_node().document().lookup_name(as_long()) :
+ as_string().c_str())
+ << "' was not found");
+ break;
+ }
- // Define the parameter so that on lookup the parser will find
- // an ARG_INDEX value.
- ptr_op_t ref(new op_t(ARG_INDEX));
- ref->set_long(index++);
+ case O_NEQ:
+ return left()->calc(scope) != right()->calc(scope);
+ case O_EQ:
+ return left()->calc(scope) == right()->calc(scope);
+ case O_LT:
+ return left()->calc(scope) < right()->calc(scope);
+ case O_LTE:
+ return left()->calc(scope) <= right()->calc(scope);
+ case O_GT:
+ return left()->calc(scope) > right()->calc(scope);
+ case O_GTE:
+ return left()->calc(scope) >= right()->calc(scope);
- assert(arg->kind == NODE_NAME);
- arg_scope.define(arg->as_string(), ref);
- }
+ case O_ADD:
+ return left()->calc(scope) + right()->calc(scope);
+ case O_SUB:
+ return left()->calc(scope) - right()->calc(scope);
+ case O_MUL:
+ return left()->calc(scope) * right()->calc(scope);
+ case O_DIV:
+ return left()->calc(scope) / right()->calc(scope);
- xpath_t rexpr(right->compile(arg_scope, resolve));
-#endif
- scope.define(left()->left()->as_string(), right());
+ case O_NEG:
+ assert(! right());
+ return left()->calc(scope).negate();
- return right();
- }
+ case O_NOT:
+ assert(! right());
+ return ! left()->calc(scope);
- case O_EVAL: {
- scope_t call_args(scope, scope_t::ARGUMENT);
-
- ptr_op_t args = right();
- while (args) {
- ptr_op_t arg = args;
- if (args->kind == O_COMMA) {
- arg = args->left();
- args = args->right();
- } else {
- args = NULL;
- }
+ case O_AND:
+ return left()->calc(scope) && right()->calc(scope);
+ case O_OR:
+ return left()->calc(scope) || right()->calc(scope);
- // jww (2006-09-15): Need to return a reference to these, if
- // there are undetermined arguments!
- call_args.args.push_back(arg->compile(context, scope, resolve)->as_value());
- }
+ case O_COMMA:
+ case O_UNION: {
+ value_t result = left()->calc(scope);
- if (left()->kind == FUNC_NAME) {
- if (resolve)
- if (optional<value_t> temp =
- scope.resolve(left()->as_string(), call_args))
- return wrap_value(*temp);
-
- // Don't compile to the left, otherwise the function name may
- // get resolved before we have a chance to call it
- xpath_t func(left()->compile(context, scope, false));
- if (func.ptr->kind == FUNCTION) {
- return wrap_value(func.ptr->as_function()(call_args));
- }
- else if (! resolve) {
- return func.ptr->compile(context, call_args, resolve);
- }
- else {
- throw_(calc_error,
- "Unknown function name '" << left()->as_string() << "'");
- }
+ ptr_op_t next = right();
+ while (next && (next->kind == O_COMMA ||
+ next->kind == O_UNION)) {
+ result.push_back(next->left()->calc(scope));
+ next = next->right();
}
- else if (left()->kind == FUNCTION) {
- return wrap_value(left()->as_function()(call_args));
- }
- else {
- assert(false);
- }
- break;
- }
+ assert(! next);
- case O_FIND:
- case O_RFIND:
- case NODE_ID:
- case NODE_NAME:
- if (resolve)
- return wrap_value(path_t(ptr_op_t(this)).find_all(context, scope));
- else
- return this;
-
- case O_PRED:
- assert(false); // this should never occur by itself
- break;
+ return result;
+ }
case LAST:
default:
- assert(false);
break;
}
-#if 0
- }
- catch (error * err) {
-#if 0
- // jww (2006-09-09): I need a reference to the parent xpath_t
- if (err->context.empty() ||
- ! dynamic_cast<context *>(err->context.back()))
- err->context.push_back(new context(this));
-#endif
- throw err;
- }
-#endif
assert(false);
- return NULL;
+ return NULL_VALUE;
}
-value_t xpath_t::calc(const node_t& context, scope_t& scope) const
+bool xpath_t::op_t::print(std::ostream& out, print_context_t& context) const
{
-#if 0
- try {
-#endif
- xpath_t final(ptr->compile(context, scope, true));
- // jww (2006-09-09): Give a better error here if this is not
- // actually a value
- return final.ptr->as_value();
-#if 0
- }
- catch (error * err) {
- if (err->context.empty() ||
- ! dynamic_cast<context *>(err->context.back()))
- err->context.push_back
- (new context(*this, ptr, "While calculating value expression:"));
-#if 0
- error_context * last = err->context.back();
- if (context * ctxt = dynamic_cast<context *>(last)) {
- ctxt->xpath = *this;
- ctxt->desc = "While calculating value expression:";
- }
-#endif
- throw err;
- }
-#endif
-}
-
-#if 0
-xpath_t::context::context(const xpath_t& _xpath,
- const ptr_op_t& _err_node,
- const string& desc) throw()
- : error_context(desc), xpath(_xpath), err_node(_err_node)
-{
-}
-
-void xpath_t::context::describe(std::ostream& out) const throw()
-{
- if (! xpath) {
- out << "xpath_t::context expr not set!" << std::endl;
- return;
- }
-
- if (! desc.empty())
- out << desc << std::endl;
-
- out << " ";
- unsigned long start = (long)out.tellp() - 1;
- unsigned long begin;
- unsigned long end;
bool found = false;
- if (xpath)
- xpath.print(out, true, err_node, &begin, &end);
- out << std::endl;
- if (found) {
- out << " ";
- for (unsigned int i = 0; i < end - start; i++) {
- if (i >= begin - start)
- out << "^";
- else
- out << " ";
- }
- out << std::endl;
- }
-}
-#endif
-bool xpath_t::op_t::print(std::ostream& out,
- document_t& document,
- const bool relaxed,
- const ptr_op_t& op_to_find,
- unsigned long * start_pos,
- unsigned long * end_pos) const
-{
- bool found = false;
-
- if (start_pos && this == op_to_find) {
- *start_pos = (long)out.tellp() - 1;
+ if (context.start_pos && this == context.op_to_find) {
+ *context.start_pos = (long)out.tellp() - 1;
found = true;
}
@@ -1607,10 +1244,10 @@ bool xpath_t::op_t::print(std::ostream& out,
break;
case value_t::INTEGER:
case value_t::AMOUNT:
- if (! relaxed)
+ if (! context.relaxed)
out << '{';
out << value;
- if (! relaxed)
+ if (! context.relaxed)
out << '}';
break;
case value_t::BALANCE:
@@ -1625,7 +1262,6 @@ bool xpath_t::op_t::print(std::ostream& out,
break;
case value_t::XML_NODE:
- case value_t::CONST_XML_NODE:
out << '<' << value << '>';
break;
case value_t::POINTER:
@@ -1642,7 +1278,15 @@ bool xpath_t::op_t::print(std::ostream& out,
out << '@';
// fall through...
case NODE_ID: {
- optional<const char *> name = document.lookup_name(as_name());
+ context_scope_t& context_scope(context.scope.find_scope<context_scope_t>());
+
+ if (context_scope.type() != scope_t::NODE_SCOPE)
+ throw_(calc_error, "Looking up node name in a non-node context");
+
+ node_scope_t& node_scope(downcast<node_scope_t>(context_scope));
+
+ optional<const char *> name =
+ node_scope.xml_node().document().lookup_name(as_name());
if (name)
out << *name;
else
@@ -1673,194 +1317,170 @@ bool xpath_t::op_t::print(std::ostream& out,
case O_NOT:
out << "!";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
break;
case O_NEG:
out << "-";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
break;
case O_UNION:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " | ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
break;
case O_ADD:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " + ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_SUB:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " - ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_MUL:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " * ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_DIV:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " / ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_NEQ:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " != ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_EQ:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " == ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_LT:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " < ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_LTE:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " <= ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_GT:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " > ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_GTE:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " >= ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_AND:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " & ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_OR:
out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << " | ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
- case O_QUES:
- out << "(";
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- out << " ? ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- out << ")";
- break;
- case O_COLON:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- out << " : ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- break;
-
case O_COMMA:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << ", ";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
break;
- case O_DEFINE:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- out << '=';
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
- found = true;
- break;
- case O_EVAL:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ case O_CALL:
+ if (left() && left()->print(out, context))
found = true;
out << "(";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << ")";
break;
case O_FIND:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << "/";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
break;
case O_RFIND:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << "//";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
break;
case O_PRED:
- if (left() && left()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (left() && left()->print(out, context))
found = true;
out << "[";
- if (right() && right()->print(out, document, relaxed, op_to_find, start_pos, end_pos))
+ if (right() && right()->print(out, context))
found = true;
out << "]";
break;
@@ -1877,8 +1497,8 @@ bool xpath_t::op_t::print(std::ostream& out,
out << symbol;
}
- if (end_pos && this == op_to_find)
- *end_pos = (long)out.tellp() - 1;
+ if (context.end_pos && this == context.op_to_find)
+ *context.end_pos = (long)out.tellp() - 1;
return found;
}
@@ -1927,6 +1547,8 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
out << "FUNCTION - " << as_function();
break;
+ case O_CALL: out << "O_CALL"; break;
+
case O_NOT: out << "O_NOT"; break;
case O_NEG: out << "O_NEG"; break;
@@ -1947,14 +1569,8 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
case O_AND: out << "O_AND"; break;
case O_OR: out << "O_OR"; break;
- case O_QUES: out << "O_QUES"; break;
- case O_COLON: out << "O_COLON"; break;
-
case O_COMMA: out << "O_COMMA"; break;
- case O_DEFINE: out << "O_DEFINE"; break;
- case O_EVAL: out << "O_EVAL"; break;
-
case O_FIND: out << "O_FIND"; break;
case O_RFIND: out << "O_RFIND"; break;
case O_PRED: out << "O_PRED"; break;
@@ -1978,164 +1594,20 @@ void xpath_t::op_t::dump(std::ostream& out, const int depth) const
}
}
-template <typename NodeType>
-void xpath_t::path_t::check_element(NodeType& start,
- const ptr_op_t& element,
- scope_t& scope,
- std::size_t index,
- std::size_t size,
- const visitor_t& func)
-{
- if (element->kind > op_t::TERMINALS &&
- element->left()->kind == op_t::O_PRED) {
- function_scope_t xpath_fscope(start, index, size, scope);
- if (! op_predicate(element->left()->right())(start, xpath_fscope))
- return;
- }
-
- if (element->kind < op_t::TERMINALS) {
- value_t temp(&start);
- assert(temp.is_xml_node());
- func(temp);
- } else {
- walk_elements<NodeType>(start, element->right(),
- element->kind == op_t::O_RFIND, scope, func);
- }
-}
+} // namespace xml
-template <typename NodeType>
-void xpath_t::path_t::walk_elements(NodeType& start,
- const ptr_op_t& element,
- const bool recurse,
- scope_t& scope,
- const visitor_t& func)
+value_t xml_command(xml::xpath_t::call_scope_t& args)
{
- ptr_op_t name(element);
+ assert(args.size() == 0);
- if (name->kind > op_t::TERMINALS &&
- (name->kind == op_t::O_FIND || name->kind == op_t::O_RFIND)) {
- name = element->left();
+ value_t ostream = args.resolve("ostream");
+ std::ostream& outs(ostream.as_ref_lval<std::ostream>());
- if (name->kind == op_t::O_PRED)
- name = name->left();
- }
-
- switch (name->kind) {
- case op_t::NODE_ID:
- switch (name->as_name()) {
- case document_t::CURRENT:
- check_element<NodeType>(start, element, scope, 0, 1, func);
- break;
-
- case document_t::PARENT:
- if (optional<parent_node_t&> parent = start.parent())
- check_element<NodeType>(*parent, element, scope, 0, 1, func);
- else
- throw_(std::logic_error, "Attempt to access parent of root node");
- break;
-
- case document_t::ROOT:
- check_element<NodeType>(start.document(), element, scope, 0, 1, func);
- break;
-
- case document_t::ALL: {
- if (! start.is_parent_node())
- throw_(compile_error, "Referencing child nodes from a non-parent value");
-
- std::size_t index = 0;
- std::size_t size = start.as_parent_node().size();
- foreach (NodeType * node, start.as_parent_node())
- check_element<NodeType>(*node, element, scope, index++, size, func);
- break;
- }
-
- default:
- break; // pass down to the NODE_NAME case
- }
- // fall through...
-
- case op_t::NODE_NAME:
- if (start.is_parent_node()) {
- bool have_name_id = name->kind == op_t::NODE_ID;
-
- std::size_t index = 0;
- std::size_t size = start.as_parent_node().size();
- foreach (NodeType * child, start.as_parent_node()) {
- if ((have_name_id && name->as_name() == child->name_id()) ||
- (! have_name_id && name->as_string() == child->name()))
- check_element<NodeType>(*child, element, scope, index++, size, func);
- else if (recurse)
- walk_elements<NodeType>(*child, element, recurse, scope, func);
- }
- }
- break;
-
- default: {
- function_scope_t xpath_fscope(start, 0, 1, scope);
- xpath_t final(name->compile(start, xpath_fscope, true));
-
- if (final.ptr->is_value()) {
- value_t& result(final.ptr->as_value());
-
- if (result.is_xml_node()) {
- check_element<NodeType>(*result.template as_xml_node<NodeType>(),
- element, scope, 0, 1, func);
- }
- else if (result.is_sequence()) {
- std::size_t index = 0;
- std::size_t size = start.as_parent_node().size();
-
- foreach (const value_t& value, result.as_sequence()) {
- if (value.is_xml_node()) {
- check_element<NodeType>(*value.template as_xml_node<NodeType>(),
- element, scope, index++, size, func);
-
- // Apply to every child if this part is recursive
- if (recurse)
- walk_elements<NodeType>(*value.template as_xml_node<NodeType>(),
- element, recurse, scope, func);
- } else {
- if (element->kind == op_t::O_FIND ||
- element->kind == op_t::O_RFIND)
- throw_(compile_error,
- "Non-final expression in XPath selection returns non-node");
-
- func(value);
- }
- }
- }
- else {
- if (element->kind == op_t::O_FIND ||
- element->kind == op_t::O_RFIND)
- throw_(compile_error,
- "Non-final expression in XPath selection returns non-node");
+ xml::xpath_t::node_scope_t& node_context
+ (FIND_SCOPE(xml::xpath_t::node_scope_t, args));
+ node_context.xml_node().print(outs);
- func(result);
- }
- } else {
- throw_(compile_error, "Expression in XPath selection is invalid");
- }
- break;
- }
- }
+ return true;
}
-template
-void xpath_t::path_t::walk_elements<node_t>
- (node_t& start,
- const ptr_op_t& element,
- const bool recurse,
- scope_t& scope,
- const visitor_t& func);
-
-template
-void xpath_t::path_t::check_element<const node_t>
- (const node_t& start,
- const ptr_op_t& element,
- scope_t& scope,
- std::size_t index,
- std::size_t size,
- const visitor_t& func);
-
-} // namespace xml
} // namespace ledger
diff --git a/src/xpath.h b/src/xpath.h
index 33424824..a34c41d0 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -51,77 +51,259 @@ public:
DECLARE_EXCEPTION(calc_error);
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) \
- xml::xpath_t::wrap_functor(bind(&x, this, _1))
-
- static ptr_op_t wrap_value(const value_t& val);
- static ptr_op_t wrap_functor(const function_t& fobj);
+ xml::xpath_t::op_t::wrap_functor(bind(&x, this, _1))
+#define WRAP_FUNCTOR(x) \
+ xml::xpath_t::op_t::wrap_functor(x)
public:
class scope_t : public noncopyable
{
+ public:
+ enum type_t {
+ CHILD_SCOPE,
+ SYMBOL_SCOPE,
+ CALL_SCOPE,
+ CONTEXT_SCOPE,
+ NODE_SCOPE,
+ PREDICATE_SCOPE
+ } type_;
+
+ explicit scope_t(type_t _type) : type_(_type) {
+ TRACE_CTOR(xpath_t::scope_t, "type_t");
+ }
+ virtual ~scope_t() {
+ TRACE_DTOR(xpath_t::scope_t);
+ }
+
+ const type_t type() const {
+ return type_;
+ }
+
+ 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 child_scope_t : public scope_t
+ {
+ scope_t * parent;
+
+ public:
+ explicit child_scope_t(type_t _type = CHILD_SCOPE)
+ : scope_t(_type), parent(NULL) {
+ TRACE_CTOR(xpath_t::child_scope_t, "type_t");
+ }
+ explicit child_scope_t(scope_t& _parent, type_t _type = CHILD_SCOPE)
+ : 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();
+ }
+
+ virtual optional<scope_t&> find_scope(type_t _type,
+ bool skip_this = false) {
+ for (scope_t * ptr = (skip_this ? parent : this); ptr; ) {
+ if (ptr->type() == _type)
+ return *ptr;
+
+ 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:
- optional<scope_t&> parent;
- value_t::sequence_t args;
+ 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);
+ }
- enum kind_t { NORMAL, STATIC, ARGUMENT } kind;
+ 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);
+ };
- 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&>&");
+ 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&");
}
- 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 ~call_scope_t() {
+ TRACE_DTOR(xpath_t::call_scope_t);
}
- virtual ~scope_t() {
- TRACE_DTOR(xpath_t::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:
- virtual void define(const string& name, ptr_op_t def);
- void define(const string& name, const function_t& def);
- virtual ptr_op_t lookup(const string& name);
+ 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);
+ }
- virtual optional<value_t> resolve(const string& name, scope_t& locals) {
- if (parent)
- return parent->resolve(name, locals);
- return none;
+ 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);
}
- friend struct op_t;
+ 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 function_scope_t : public scope_t
+ class node_scope_t : public context_scope_t
{
- const node_t& node;
- std::size_t index;
- std::size_t size;
+ 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:
- function_scope_t(const value_t::sequence_t& _sequence,
- const node_t& _node,
- std::size_t _index,
- const optional<scope_t&>& _parent = none)
- : scope_t(_parent, STATIC), node(_node), index(_index),
- size(_sequence.size()) {}
-
- function_scope_t(const node_t& _node,
- std::size_t _index,
- std::size_t _size,
- const optional<scope_t&>& _parent = none)
- : scope_t(_parent, STATIC), node(_node), index(_index),
- size(_size) {}
-
- virtual optional<value_t> resolve(const string& name, scope_t& locals);
+ 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
@@ -137,57 +319,59 @@ private:
struct token_t
{
enum kind_t {
- IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
VALUE, // any kind of literal value
- AT_SYM, // @
+
+ IDENT, // [A-Za-z_][-A-Za-z0-9_:]*
DOLLAR, // $
+ AT_SYM, // @
+
DOT, // .
DOTDOT, // ..
+ SLASH, // /
+
LPAREN, // (
RPAREN, // )
- LBRACKET, // (
- RBRACKET, // )
- EXCLAM, // !
- NEQUAL, // !=
- MINUS, // -
- PLUS, // +
- STAR, // *
- POWER, // **
- SLASH, // /
+ LBRACKET, // [
+ RBRACKET, // ]
+
EQUAL, // =
- ASSIGN, // :=
+ NEQUAL, // !=
LESS, // <
LESSEQ, // <=
GREATER, // >
GREATEREQ, // >=
- AMPER, // &
- PIPE, // |
- QUESTION, // ?
- COLON, // :
- COMMA, // ,
+
+ MINUS, // -
+ PLUS, // +
+ STAR, // *
+ KW_DIV,
+
+ EXCLAM, // !
KW_AND,
KW_OR,
- KW_DIV,
KW_MOD,
+
+ PIPE, // |
KW_UNION,
+
+ COMMA, // ,
+
TOK_EOF,
UNKNOWN
} kind;
- char symbol[3];
- value_t value;
- unsigned int length;
+ char symbol[3];
+ value_t value;
+ std::size_t length;
- token_t() : kind(UNKNOWN), length(0) {
+ explicit token_t() : kind(UNKNOWN), length(0) {
TRACE_CTOR(xpath_t::token_t, "");
}
-
token_t(const token_t& other) {
assert(false);
TRACE_CTOR(xpath_t::token_t, "copy");
*this = other;
}
-
~token_t() {
TRACE_DTOR(xpath_t::token_t);
}
@@ -202,7 +386,7 @@ private:
void clear() {
kind = UNKNOWN;
length = 0;
- value = 0L;
+ value = NULL_VALUE;
symbol[0] = '\0';
symbol[1] = '\0';
@@ -210,82 +394,23 @@ private:
}
void parse_ident(std::istream& in);
-
void next(std::istream& in, flags_t flags);
void rewind(std::istream& in);
-
void unexpected();
+
static void unexpected(char c, char wanted = '\0');
};
public:
- class path_t
- {
- 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>
+#if 0
class path_iterator_t
{
typedef NodeType * pointer;
typedef NodeType& reference;
- path_t path;
- reference start;
- scope_t& scope;
+ path_t path;
+ node_t& start;
+ scope_t& scope;
mutable value_t::sequence_t sequence;
mutable bool searched;
@@ -304,7 +429,7 @@ public:
typedef value_t::sequence_t::const_iterator const_iterator;
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),
searched(false) {
}
@@ -323,21 +448,21 @@ public:
iterator end() { return sequence.end(); }
const_iterator end() const { return sequence.end(); }
};
+#endif
struct op_t : public noncopyable
{
enum kind_t {
- VOID,
VALUE,
+ FUNC_NAME,
+ VAR_NAME,
+ ARG_INDEX,
+
NODE_ID,
NODE_NAME,
ATTR_ID,
ATTR_NAME,
- FUNC_NAME,
- VAR_NAME,
-
- ARG_INDEX,
CONSTANTS, // constants end here
@@ -345,15 +470,12 @@ public:
TERMINALS, // terminals end here
- O_NOT,
- O_NEG,
-
- O_UNION,
+ O_CALL,
+ O_ARG,
- O_ADD,
- O_SUB,
- O_MUL,
- O_DIV,
+ O_FIND,
+ O_RFIND,
+ O_PRED,
O_NEQ,
O_EQ,
@@ -362,22 +484,20 @@ public:
O_GT,
O_GTE,
+ O_ADD,
+ O_SUB,
+ O_MUL,
+ O_DIV,
+ O_NEG,
+
+ O_NOT,
O_AND,
O_OR,
- O_QUES,
- O_COLON,
+ O_UNION,
O_COMMA,
- O_DEFINE,
- O_EVAL,
- O_ARG,
-
- O_FIND,
- O_RFIND,
- O_PRED,
-
LAST // operators end here
};
@@ -387,13 +507,13 @@ public:
variant<unsigned int, // used by ARG_INDEX and O_ARG
value_t, // used by constant VALUE
- string, // used by constant SYMBOL
+ string, // used by constants SYMBOL, *_NAME
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
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");
}
~op_t() {
@@ -403,8 +523,6 @@ public:
assert(refc == 0);
}
- op_t& operator=(const op_t&);
-
bool is_long() const {
return data.type() == typeid(unsigned int);
}
@@ -475,22 +593,6 @@ public:
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() {
assert(kind > TERMINALS);
return boost::get<ptr_op_t>(data);
@@ -538,23 +640,38 @@ public:
data = expr;
}
- static ptr_op_t new_node(kind_t kind, ptr_op_t left = NULL,
- ptr_op_t right = NULL);
+ static ptr_op_t new_node(kind_t _kind, ptr_op_t _left = 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;
- ptr_op_t compile(const node_t& context, scope_t& scope, bool resolve = false);
+ static ptr_op_t wrap_value(const value_t& val);
+ 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,
- document_t& document,
- const bool relaxed = true,
- const ptr_op_t& op_to_find = NULL,
- unsigned long * start_pos = NULL,
- unsigned long * end_pos = NULL) const;
+ print_context_t(scope_t& _scope,
+ const bool _relaxed = false,
+ const ptr_op_t& _op_to_find = ptr_op_t(),
+ unsigned long * _start_pos = NULL,
+ unsigned long * _end_pos = NULL)
+ : 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;
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;
public:
explicit op_predicate(ptr_op_t _op) : op(_op) {}
-
- bool operator()(const node_t& node, scope_t& scope) {
- xpath_t result(op->compile(node, scope, true));
- return result.ptr->as_value().to_boolean();
+ bool operator()(scope_t& scope) const {
+ return op->calc(scope).to_boolean();
}
};
@@ -660,14 +774,9 @@ public:
return parse_expr(string(p), tflags);
}
- bool print(std::ostream& out,
- document_t& document,
- const bool relaxed,
- const ptr_op_t op_to_find,
- unsigned long * start_pos,
- unsigned long * end_pos) const {
+ bool print(std::ostream& out, op_t::print_context_t& context) const {
if (ptr)
- ptr->print(out, document, relaxed, op_to_find, start_pos, end_pos);
+ ptr->print(out, context);
return true;
}
@@ -675,20 +784,20 @@ public:
string 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, "");
}
- 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");
}
- 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) {
TRACE_CTOR(xpath_t, "const string&, flags_t");
if (! _expr.empty())
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) {
TRACE_CTOR(xpath_t, "std::istream&, flags_t");
parse(in, _flags);
@@ -698,33 +807,29 @@ public:
expr(other.expr), flags(other.flags) {
TRACE_CTOR(xpath_t, "copy");
}
- virtual ~xpath_t() {
+ ~xpath_t() {
TRACE_DTOR(xpath_t);
}
+#if 0
xpath_t& operator=(const string& _expr) {
parse(_expr);
return *this;
}
+#endif
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() {
return ptr;
}
-
operator bool() const throw() {
return ptr != NULL;
}
operator string() const throw() {
return expr;
}
+#endif
void parse(const string& _expr, flags_t _flags = XPATH_PARSE_RELAXED) {
expr = _expr;
@@ -737,18 +842,22 @@ public:
ptr = parse_expr(in, _flags);
}
- void compile(const node_t& context, scope_t& scope) {
+ void compile(scope_t& scope) {
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())
+ return ptr->calc(scope);
+ return NULL_VALUE;
+ }
- static value_t eval(const string& _expr, const node_t& context,
- scope_t& scope) {
- 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>
find_all(node_t& start, scope_t& scope) {
return path_iterator_t<node_t>(*this, start, scope);
@@ -765,47 +874,81 @@ public:
path_t::visitor_t& func) {
path_t(*this).visit(start, scope, func);
}
+#endif
- void print(std::ostream& out, xml::document_t& document) const {
- print(out, document, true, NULL, NULL, NULL);
+ void print(std::ostream& out, scope_t& scope) const {
+ op_t::print_context_t context(scope);
+ print(out, context);
}
void dump(std::ostream& out) const {
if (ptr)
ptr->dump(out, 0);
}
-
- friend class scope_t;
};
-} // namespace xml
+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;
+}
-template <typename T>
-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;
+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;
}
-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;
+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;
}
-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;
- }
-};
+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
+
+value_t xml_command(xml::xpath_t::call_scope_t& args);
} // namespace ledger