diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/commodity.cc | 4 | ||||
-rw-r--r-- | src/main.cc | 35 | ||||
-rw-r--r-- | src/node.cc | 9 | ||||
-rw-r--r-- | src/node.h | 5 | ||||
-rw-r--r-- | src/option.cc | 6 | ||||
-rw-r--r-- | src/pyinterp.cc | 20 | ||||
-rw-r--r-- | src/pyinterp.h | 21 | ||||
-rw-r--r-- | src/report.cc | 49 | ||||
-rw-r--r-- | src/report.h | 64 | ||||
-rw-r--r-- | src/session.cc | 54 | ||||
-rw-r--r-- | src/session.h | 62 | ||||
-rw-r--r-- | src/transform.h | 30 | ||||
-rw-r--r-- | src/utils.h | 5 | ||||
-rw-r--r-- | src/value.h | 134 | ||||
-rw-r--r-- | src/xpath.cc | 1136 | ||||
-rw-r--r-- | src/xpath.h | 643 |
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++) { @@ -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 ¤t_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 |