Commit Graph

283 Commits

Author SHA1 Message Date
Yorick Peterse 873bd82273 Stricted matching of namespaced elements. 2014-08-11 00:47:07 +02:00
Yorick Peterse 33c28f633b Proper namespace support for elements.
This is still a bit rough on the edges but already way better than the broken
setup I had before.
2014-08-11 00:41:36 +02:00
Yorick Peterse 04cbbdcf9e Proper namespace support for attributes.
This separates namespace handling into namespace names and namespace objects.
The namespace objects are retrieved from the element an attribute belongs to.
Once retrieved the namespace is cached, due to the overhead of retrieving
namespaces in large documents.
2014-08-11 00:40:17 +02:00
Yorick Peterse fe8f77cf45 Basic work for supporting namespace URIs. 2014-08-08 19:03:42 +02:00
Yorick Peterse f002061aaa Extra type validation for XML::Element options. 2014-08-07 21:10:01 +02:00
Yorick Peterse b1388ff84a Ripped out inspect fuckery.
The old code used for generating Object#inspect values has been ripped out (for
the most part). The result is a non indented but far more compact #inspect
output. The code for this is also easier and doesn't break the signature of
Object#inspect.
2014-08-07 21:09:10 +02:00
Yorick Peterse 3b2279e410 Don't create empty Namespace nodes. 2014-08-07 20:16:46 +02:00
Yorick Peterse 4e18989972 Remove the uri attribute from Namespace.
Oga won't be handling URIs any time soon. The rationale is that they server zero
purpose when it comes to just parsing XML. Another goal of Oga is to make it
easy to modify and reserialize documents back to XML. If namespaces would also
store the URIs this would make this process more difficult.
2014-08-07 20:11:17 +02:00
Yorick Peterse 97e59fe449 Use the Namespace class for namespaces vs Strings. 2014-08-07 20:03:26 +02:00
Yorick Peterse f653203220 Tests for the Namespace class. 2014-08-07 20:02:56 +02:00
Yorick Peterse 8e8ea64206 Fixed serializing of elements to XML. 2014-08-06 00:04:42 +02:00
Yorick Peterse e0bbc81351 Added a very basic Namespace class. 2014-08-06 00:00:08 +02:00
Yorick Peterse d7df908649 Trimmed XML inspect values. 2014-08-05 23:57:12 +02:00
Yorick Peterse 26d4bdc5b1 Support for the XPath "self" axis. 2014-08-05 21:10:12 +02:00
Yorick Peterse 8a9b26fa73 Basic support for the preceding-sibling xpath axis 2014-08-05 19:28:26 +02:00
Yorick Peterse fc1d9776f3 Basic support for the XPath "preceding" axis. 2014-08-05 10:16:37 +02:00
Yorick Peterse 375f3d7870 Basic support for the XPath "parent" axis.
The usage of `parent::node()` is not yet supported.
2014-08-05 09:34:57 +02:00
Yorick Peterse c0a6610d65 Use has_parent? in on_axis_following_sibling. 2014-08-04 21:57:16 +02:00
Yorick Peterse a1f80b4995 Support for the "following-sibling" axis.
This also comes with some small cleanups regarding
XPath::Evaluator#node_matches?. This change removes the need to, every time,
also use can_match_node?() to prevent NoMethodError errors from popping up.
2014-08-04 21:51:51 +02:00
Yorick Peterse 57c0f4b35e Renamed `node` to `ast_node`.
This should make it a bit easier to understand what kind of data the variable is
holding.
2014-08-04 19:01:27 +02:00
Yorick Peterse 211caf00c6 Proper support for the XPath "following" axis. 2014-08-04 18:57:21 +02:00
Yorick Peterse 57fcbbd0fc Allow Document#each_node to skip child nodes.
Child nodes can be skipped by throwing :skip_children.
2014-08-04 10:00:32 +02:00
Yorick Peterse ef1ad5406a Don't yield indexes in Document#each_node.
These indexes won't be used so there's no point in yielding them.
2014-08-04 09:08:39 +02:00
Yorick Peterse 5c23333f46 Traverse document nodes in document order.
The method Document#each_node now yields the nodes in the correct order.
2014-08-01 23:34:32 +02:00
Yorick Peterse c419d8849b Shift instead of pop nodes when yielding all nodes 2014-08-01 19:00:29 +02:00
Yorick Peterse 34e2d28bbd Document#all_nodes -> Document#each_node
This method has been renamed and now yields nodes and their indexes instead of
buffering them in a node set.
2014-07-31 18:57:05 +02:00
Yorick Peterse 4bbf0c98ae Use breadth-first-search for returning all nodes.
This still uses a stack but at least no longer relies on the call stack. I
decided not to go with the Morris in-order algorithm [1] as it modifies the tree
during a search. This would not work well if a document were to be accessed from
multiple threads at once (which should be possible for read-only operations).

I might change this method to actually perform a search (opposed to just
returning everything). This will require some closer inspection of the
available XPath axes to determine if this is needed.

Tests will also be added once I've taken care of the above.

[1]: http://en.wikipedia.org/wiki/Tree_traversal#Morris_in-order_traversal_using_threading
2014-07-30 22:27:09 +02:00
Yorick Peterse 8fe71f298b Half-assed way of retrieving all document nodes.
This currently only works for documents, is not tested and most likely will leak
memory due to being recursive.
2014-07-30 19:56:56 +02:00
Yorick Peterse 52a4375278 Prepare setup for *actual* following support.
The previous commit was nonsense as I didn't understand XPath's "following" axis
properly. This commit introduces proper tests and a note for future me so that I
can implement it properly.
2014-07-30 00:16:44 +02:00
Yorick Peterse 9a97d936e3 Support for the XPath "following" axis. 2014-07-29 23:09:16 +02:00
Yorick Peterse 55e3388e30 Unfuck XPath axes evaluation.
The evaluation of axes has been fixed by changing the initial context as well as
the behaviour of some of the handler methods.

The initial context has been changed so that it's simply a NodeSet of whatever
the root object is, either a Document or an Element instance. Previously this
would be set to the child nodes of a Document in case the root object was a
Document. This in turn would prevent "child" axes from operating correctly.
2014-07-28 00:44:05 +02:00
Yorick Peterse 23de57a3a0 Parse bare XPath node tests as child axes.
When parsing a bare node test such as "A" this is now parsed as following:

    (axis "child" (test nil "A"))

Instead of this:

    (test nil "A")

According to the XPath specification both are identical and this simplifies some
of the code in the XPath evaluator.
2014-07-28 00:34:26 +02:00
Yorick Peterse 1916799fef Basic boilerplate for descendant-or-self. 2014-07-25 21:24:39 +02:00
Yorick Peterse dd37b028a0 Support for the XPath descendant axis. 2014-07-24 09:49:05 +02:00
Yorick Peterse fd37bcff1f Corrected an XPath example. 2014-07-23 18:48:53 +02:00
Yorick Peterse 54e109bf97 Corrected various YARD tags. 2014-07-22 21:28:44 +02:00
Yorick Peterse a0ecba6321 Support for the XPath child axis. 2014-07-22 21:25:02 +02:00
Yorick Peterse ec08b41737 Specs for various XPath::Evaluator helper methods. 2014-07-22 21:04:09 +02:00
Yorick Peterse 1f9d2ede95 Skip node matching for incompatible nodes. 2014-07-22 20:45:15 +02:00
Yorick Peterse 05cf07755d Basic docs for the XPath evaluator. 2014-07-22 16:12:21 +02:00
Yorick Peterse 4142504101 Revert "Lex XPath axes without a node test."
Upon further investigation this change turned out to be useless. Nokogiri/libxml
does not allow the use of long axes without tests, instead it ends up
lexing/parsing such a value as a simple node test.

This reverts commit f699b0d097.
2014-07-22 15:53:00 +02:00
Yorick Peterse c43c38fab9 Emit node() calls for certain short axes.
An axes such as "." is the same as "self::node()". To simplify things on
parser/evaluator level we'll emit the corresponding tokens for a "node()"
function call for these axes.
2014-07-22 15:50:39 +02:00
Yorick Peterse f699b0d097 Lex XPath axes without a node test. 2014-07-20 07:57:45 +02:00
Yorick Peterse 488000438b Support for querying attributes using XPath. 2014-07-20 07:47:01 +02:00
Yorick Peterse d5569ead0b Use XML::Attribute for element attributes.
Instead of using a raw Hash Oga now uses the XML::Attribute class for storing
information about element attributes.

Attributes are stored as an Array of XML::Attribute instances. This allows the
attributes to be more easily modified. If they were stored as a Hash you'd not
only have to update the attributes themselves but also the Hash that contains
them.

While using an Array has a slight runtime cost in most cases the amount of
attributes is small enough that this doesn't really pose a problem. If webscale
performance is desired at some point in the future Oga could most likely cache
the lookup of an attribute. This however is something for the future.
2014-07-20 07:29:37 +02:00
Yorick Peterse ce86785da6 Added the XML::Attribute class.
This class will replace the use of raw Hash/String values for attributes in
upcoming commits.
2014-07-16 10:08:11 +02:00
Yorick Peterse d09ab26680 Support for the ancestor-or-self axis. 2014-07-15 09:41:12 +02:00
Yorick Peterse 0211f60826 Evaluation of XPath "ancestor" axes. 2014-07-14 09:43:30 +02:00
Yorick Peterse 03f40d4024 Ripped out internal state of XPath::Evaluator.
Instead of keeping track of an internal state in @stack and @context the various
processing methods now take the context as an extra argument and return the
nodes they produced. This makes it easier to recursively call certain methods, a
requirement for processing XPath axes (e.g. the "ancestor" axis).
2014-07-14 00:07:43 +02:00
Yorick Peterse 56982dd543 Fail early when processing (test) nodes.
If a certain segment yields no nodes the on_path() method should not process any
following segments.
2014-07-12 00:22:18 +02:00
Yorick Peterse d6aec6aa16 Compare nodes in on_path instead of indexes.
This saves some code and effectively does the same thing.
2014-07-12 00:20:19 +02:00
Yorick Peterse 5808ffa7a4 Updated XPath evaluator for the new AST.
This is still a bit of a hack. Then again, it already was a hack to begin with.
2014-07-12 00:18:32 +02:00
Yorick Peterse be4f4ad744 Trimmed the XPath AST even further.
The excessive use of (path) nodes has been dropped and all parsing conflicts
have been dealt with.
2014-07-12 00:13:25 +02:00
Yorick Peterse aeb4eba260 Trim the XPath AST.
The AST has been simplified by adjusting the way (path) nodes are nested.
Operators now also use `paths` instead of `expression` to allow for expressions
such as `/A or /B`. Sadly this introduces quite a bunch of conflicts in the
parser but we'll deal with those later if needed.
2014-07-10 20:19:23 +02:00
Yorick Peterse 8fbc582547 XPath evaluation for name/namespace wildcards. 2014-07-09 22:09:20 +02:00
Yorick Peterse ed45058983 Basic support for evaluating XPath wildcards. 2014-07-09 20:06:31 +02:00
Yorick Peterse 74130df40d XPath evaluation support for absolute paths. 2014-07-09 09:28:00 +02:00
Yorick Peterse a398af19fe Use a stack based XPath evaluator.
This evaluator is capable of processing very, very basic XPath expressions. It
does not yet know anything about axes, functions, operators, etc.
2014-07-08 23:57:21 +02:00
Yorick Peterse 9c661e1e60 Added XML::NodeSet#+ and XML::NodeSet#to_a 2014-07-08 23:25:09 +02:00
Yorick Peterse 266c66569e Removed internal state of XPath::Evaluator.
Come to think of it it might actually be easier to implement the evaluator as
an actual VM. That is, instead of directly running on the AST it runs on some
flavour of bytecode. Alternatively it runs directly on the AST but behaves more
like a (stack based) VM. This would most likely be easier than passing a cursor
to every node processing method.
2014-07-08 19:19:59 +02:00
Yorick Peterse 808e1e8c47 Initial, half-assed attempt at an XPath evaluator. 2014-07-07 19:41:09 +02:00
Yorick Peterse 8b381ac970 Added Node#remove.
This method can be used to remove individual nodes without first having to
retrieve the NodeSet they are stored in.
2014-07-04 10:26:41 +02:00
Yorick Peterse e334e50ca6 Added Node#previous_element and Node#next_element.
These methods can be used similar to #previous and #next expect that they only
return Element instances opposed to all Node instances.
2014-07-04 10:18:18 +02:00
Yorick Peterse 94965961ce Added XML::Element#inner_text.
This method can be used to retrieve the text of the given node only. In other
words, unlike Element#text it does not contain the text of any child nodes.
2014-07-04 09:50:47 +02:00
Yorick Peterse a15c19c4af Retrieving root nodes using Node#root_node.
This method uses a loop to traverse upwards the DOM tree in order to find the
root document/element. While this might have an impact on performance I don't
expect Oga itself to call this method very often. The benefit is that Node
instances don't require users to manually pass the top level document as an
argument.
2014-07-03 00:26:15 +02:00
Yorick Peterse e69fbc3ea7 Remove ownership when using NodeSet#delete. 2014-07-01 10:15:39 +02:00
Yorick Peterse 4e2b7f529d Cleared up docs a bit of XML::Node. 2014-07-01 10:03:04 +02:00
Yorick Peterse 30845e3d65 Reliably remove nodes from owned sets.
The combination of iterating over an array and removing values from it results
in not all elements being removed. For example:

    numbers = [10, 20, 30]

    numbers.each do |num|
      numbers.delete(num)
    end

    numbers # => [20]

As a result of this the NodeSet#remove method uses two iterations:

1. One iteration to retrieve all NodeSet instances to remove nodes from.
2. One iteration to actually remove the nodes.

For the second iteration we iterate over the node sets and then over the nodes.
This ensures that we always remove all nodes instead of leaving some behind.
2014-07-01 09:57:43 +02:00
Yorick Peterse 5073056831 Added require() for StringIO.
This is needed since the lexer checks if the input is a StringIO instance.
2014-07-01 09:37:52 +02:00
Yorick Peterse 8314d24435 First pass at using the NodeSet class.
The documentation still leaves a lot to be desired and so does the API. There
also appears to be a problem where NodeSet#remove doesn't properly remove all
nodes from a set. Outside of that we're making slow progress towards a proper
DOM API.
2014-06-30 23:03:48 +02:00
Yorick Peterse e71fe3d6fa Indexing of NodeSet instances.
Similar to arrays the NodeSet class now allows one to retrieve nodes for a
given index.
2014-06-29 23:59:27 +02:00
Yorick Peterse 253575dc37 Basic docs for the NodeSet class. 2014-06-29 21:34:18 +02:00
Yorick Peterse d2e74d8a0b Specs for the NodeSet class. 2014-06-26 19:52:03 +02:00
Yorick Peterse eb9d4fbccc Changed NodeSet to behave more like an Array. 2014-06-26 09:37:54 +02:00
Yorick Peterse a98f50b63b NodeSet#push should not take node ownership. 2014-06-26 09:37:30 +02:00
Yorick Peterse 15fa7a2068 Remove explicit index tracking of NodeSet.
Instead the various nodes can use NodeSet#index (aka Array#index) instead. This
has a slight performance overhead on very large (millions) of nodes but should
be fine in most other cases.
2014-06-25 09:41:58 +02:00
Yorick Peterse 884dbd9563 Rough sketch for a NodeSet class. 2014-06-24 19:06:45 +02:00
Yorick Peterse b8cc6b5031 XPath::Parser#parse returns a XPath::Node instance 2014-06-23 20:23:08 +02:00
Yorick Peterse 03be9f241c Docs on Racc operator precedence. 2014-06-23 10:36:42 +02:00
Yorick Peterse 96a7a40fdc Disable code coverage for JRuby specific code. 2014-06-23 09:42:14 +02:00
Yorick Peterse d2f15e37d0 Corrected XPath operator precedence.
The previous commit didn't fully change the operator precedence according to
the XPath 1.0 specification. Also thanks to @whitequark for clearing up a few
things about Racc's operator precedence system.
2014-06-23 00:30:42 +02:00
Yorick Peterse a440d3f003 Fixed XPath operator precedence.
Apparently using multiple `left` rules with T_AND and T_OR being separate
solves this problem. Riiiiight....
2014-06-23 00:15:43 +02:00
Yorick Peterse 6a2f4fa82d Parsing support for more XPath operators.
This still messes up some tests due to botched token precedence (by the looks
of it).
2014-06-22 21:27:53 +02:00
Yorick Peterse 514c342cab Lex wildcards as T_IDENT instead of T_STAR. 2014-06-20 20:37:34 +02:00
Yorick Peterse 45db337c76 Use Array#unshift for multiple xpath call args. 2014-06-17 20:12:25 +02:00
Yorick Peterse b3ffc28cc7 Removed shift/reduce conflict in the xpath parser. 2014-06-17 20:09:44 +02:00
Yorick Peterse 497f57ccd2 Basic parser setup for XPath function calls. 2014-06-17 19:57:17 +02:00
Yorick Peterse 894de7f909 Lex all XPath expressions in a single machine.
This allows literal values such as strings and numbers to be used as function
arguments.
2014-06-17 19:56:57 +02:00
Yorick Peterse bb7af98257 Updated used ASTs for all XPath parser specs. 2014-06-17 18:51:33 +02:00
Yorick Peterse 2298ef618b Reworked handling of relative vs absolute XPaths. 2014-06-16 20:19:39 +02:00
Yorick Peterse eba2d9954d Support for parsing basic XPath expressions. 2014-06-12 00:20:46 +02:00
Yorick Peterse 70f3b7fa92 Lex XPath operators using individual tokens.
Instead of lexing every operator as T_OP they now use individual tokens such as
T_EQ and T_LT.
2014-06-09 23:35:54 +02:00
Yorick Peterse 7244e28eec Corrected docs of the xpath lexer. 2014-06-04 19:33:46 +02:00
Yorick Peterse 1d2f9e6db6 Added T_STAR as an XPath parser token. 2014-06-02 09:27:25 +02:00
Yorick Peterse e11b9ed32c Basic XPath parser setup. 2014-06-01 23:02:28 +02:00
Yorick Peterse 54de2df0c7 Support for lexing XPath wildcard expressions.
To support this we need to require whitespace around the "*" operator. This is
not ideal but it will do for now.
2014-06-01 23:01:24 +02:00
Yorick Peterse 8dd8d7a519 Basic working XPath lexer.
This doesn't lex everything of the XPath specification just yet and needs more
tests.
2014-06-01 19:24:35 +02:00
Yorick Peterse a50b76a2d8 Cleaned up XPath lexer boilerplate a bit. 2014-05-29 19:25:49 +02:00
Yorick Peterse e0b07332d9 Boilerplate for the XPath lexer. 2014-05-29 19:25:49 +02:00
Yorick Peterse be3f8fb494 Removed the on_newline XML lexer callback. 2014-05-29 14:21:48 +02:00