summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pugixml.cpp119
-rw-r--r--src/pugixml.hpp7
2 files changed, 111 insertions, 15 deletions
diff --git a/src/pugixml.cpp b/src/pugixml.cpp
index c7e7fd6..1a69a8e 100644
--- a/src/pugixml.cpp
+++ b/src/pugixml.cpp
@@ -5385,6 +5385,7 @@ namespace
template <typename T> T* new_xpath_variable(const char_t* name)
{
size_t length = strlength(name);
+ if (length == 0) return 0; // empty variable names are invalid
// $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
void* memory = global_allocate(sizeof(T) + length * sizeof(char_t));
@@ -5448,6 +5449,32 @@ namespace
assert(false);
}
}
+
+ xpath_variable* get_variable(xpath_variable_set* set, const char_t* begin, const char_t* end)
+ {
+ char_t buffer[32];
+
+ size_t length = static_cast<size_t>(end - begin);
+ char_t* scratch = buffer;
+
+ if (length >= sizeof(buffer) / sizeof(buffer[0]))
+ {
+ // need to make dummy on-heap copy
+ scratch = static_cast<char_t*>(global_allocate((length + 1) * sizeof(char_t)));
+ if (!scratch) return 0;
+ }
+
+ // copy string to zero-terminated buffer and perform lookup
+ memcpy(scratch, begin, length * sizeof(char_t));
+ scratch[length] = 0;
+
+ xpath_variable* result = set->get(scratch);
+
+ // free dummy buffer
+ if (scratch != buffer) global_deallocate(scratch);
+
+ return result;
+ }
}
namespace pugi
@@ -6125,6 +6152,7 @@ namespace pugi
ast_filter_posinv, // select * from left where right; proximity position invariant
ast_string_constant, // string constant
ast_number_constant, // number constant
+ ast_variable, // variable
ast_func_last, // last()
ast_func_position, // position()
ast_func_count, // count(left)
@@ -6223,6 +6251,8 @@ namespace pugi
const char_t* string;
// value for ast_number_constant
double number;
+ // variable for ast_variable
+ xpath_variable* variable;
// node test for ast_step (node name/namespace/node type/pi target)
const char_t* nodetest;
} _data;
@@ -6838,6 +6868,13 @@ namespace pugi
_data.number = value;
}
+ xpath_ast_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value):
+ _type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(0), _right(0), _next(0)
+ {
+ assert(type == ast_variable);
+ _data.variable = value;
+ }
+
xpath_ast_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0):
_type((char)type), _rettype((char)rettype), _axis(0), _test(0), _left(left), _right(right), _next(0)
{
@@ -6940,6 +6977,16 @@ namespace pugi
return false;
}
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_boolean)
+ return _data.variable->get_boolean();
+
+ // fallthrough to type conversion
+ }
+
default:
{
switch (_rettype)
@@ -7036,6 +7083,16 @@ namespace pugi
case ast_func_round:
return round_nearest_nzero(_left->eval_number(c));
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_number)
+ return _data.variable->get_number();
+
+ // fallthrough to type conversion
+ }
+
default:
{
switch (_rettype)
@@ -7211,6 +7268,16 @@ namespace pugi
return s;
}
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_string)
+ return xpath_string_const(_data.variable->get_string());
+
+ // fallthrough to type conversion
+ }
+
default:
{
switch (_rettype)
@@ -7347,6 +7414,16 @@ namespace pugi
return ns;
}
+ case ast_variable:
+ {
+ assert(_rettype == _data.variable->type());
+
+ if (_rettype == xpath_type_node_set)
+ return _data.variable->get_node_set();
+
+ // fallthrough to type conversion
+ }
+
default:
assert(!"Wrong expression for return type node set");
return xpath_node_set();
@@ -7362,7 +7439,7 @@ namespace pugi
case ast_string_constant:
case ast_number_constant:
- // $$ case ast_variable:
+ case ast_variable:
return true;
case ast_step:
@@ -7394,7 +7471,10 @@ namespace pugi
{
xpath_allocator& _alloc;
xpath_lexer _lexer;
+
const char_t* _query;
+ xpath_variable_set* _variables;
+
xpath_parse_result* _result;
jmp_buf _error_handler;
@@ -7658,9 +7738,24 @@ namespace pugi
{
case lex_var_ref:
{
- throw_error("Variables are not supported");
+ _lexer.next();
- return 0;
+ if (_lexer.current() != lex_string)
+ throw_error("Variable name expected");
+
+ if (!_variables)
+ throw_error("Unknown variable: variable set is not provided");
+
+ xpath_lexer_string name = _lexer.contents();
+
+ xpath_variable* var = get_variable(_variables, name.begin, name.end);
+
+ if (!var)
+ throw_error("Unknown variable: variable set does not contain the given name");
+
+ _lexer.next();
+
+ return new (alloc_node()) xpath_ast_node(ast_variable, var->type(), var);
}
case lex_open_brace:
@@ -8190,7 +8285,7 @@ namespace pugi
return parse_or_expression();
}
- xpath_parser(const char_t* query, xpath_allocator& alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _result(result)
+ xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator& alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
{
}
@@ -8207,9 +8302,9 @@ namespace pugi
return result;
}
- static xpath_ast_node* parse(const char_t* query, xpath_allocator& alloc, xpath_parse_result* result)
+ static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator& alloc, xpath_parse_result* result)
{
- xpath_parser parser(query, alloc, result);
+ xpath_parser parser(query, variables, alloc, result);
int error = setjmp(parser._error_handler);
@@ -8404,7 +8499,7 @@ namespace pugi
return find(name);
}
- xpath_query::xpath_query(const char_t* query): _alloc(0), _root(0)
+ xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _alloc(0), _root(0)
{
_result.error = 0;
_result.offset = 0;
@@ -8417,7 +8512,7 @@ namespace pugi
}
else
{
- _root = xpath_parser::parse(query, *_alloc, &_result);
+ _root = xpath_parser::parse(query, variables, *_alloc, &_result);
}
if (!_root)
@@ -8519,9 +8614,9 @@ namespace pugi
return !_root;
}
- xpath_node xml_node::select_single_node(const char_t* query) const
+ xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
{
- xpath_query q(query);
+ xpath_query q(query, variables);
return select_single_node(q);
}
@@ -8531,9 +8626,9 @@ namespace pugi
return s.empty() ? xpath_node() : s.first();
}
- xpath_node_set xml_node::select_nodes(const char_t* query) const
+ xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
{
- xpath_query q(query);
+ xpath_query q(query, variables);
return select_nodes(q);
}
diff --git a/src/pugixml.hpp b/src/pugixml.hpp
index 7e09551..20e8716 100644
--- a/src/pugixml.hpp
+++ b/src/pugixml.hpp
@@ -325,6 +325,7 @@ namespace pugi
class xpath_node;
class xpath_node_set;
class xpath_query;
+ class xpath_variable_set;
#endif
/**
@@ -1192,7 +1193,7 @@ namespace pugi
* \param query - query string
* \return first node from the resulting node set by document order, or empty node if none found
*/
- xpath_node select_single_node(const char_t* query) const;
+ xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
/**
* Select single node by evaluating XPath query
@@ -1208,7 +1209,7 @@ namespace pugi
* \param query - query string
* \return resulting node set
*/
- xpath_node_set select_nodes(const char_t* query) const;
+ xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
/**
* Select node set by evaluating XPath query
@@ -1889,7 +1890,7 @@ namespace pugi
*
* \param query - string with XPath expression
*/
- explicit xpath_query(const char_t* query);
+ explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
/**
* Destructor