Removed start/end comments of YARD blocks
This commit is contained in:
parent
929a521641
commit
94f8ed5421
|
@ -1,38 +1,28 @@
|
|||
module Oga
|
||||
##
|
||||
# @api private
|
||||
#
|
||||
class Blacklist
|
||||
# @return [Set]
|
||||
attr_reader :names
|
||||
|
||||
##
|
||||
# @param [Array] names
|
||||
#
|
||||
def initialize(names)
|
||||
@names = Set.new(names + names.map(&:upcase))
|
||||
end
|
||||
|
||||
##
|
||||
# @yieldparam [String]
|
||||
#
|
||||
def each
|
||||
names.each do |value|
|
||||
yield value
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def allow?(name)
|
||||
!names.include?(name)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::Blacklist] other
|
||||
# @return [Oga::Blacklist]
|
||||
#
|
||||
def +(other)
|
||||
self.class.new(names + other.names)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
module Oga
|
||||
module CSS
|
||||
##
|
||||
# Lexer for turning CSS expressions into a sequence of tokens. Tokens are
|
||||
# returned as arrays with every array having two values:
|
||||
#
|
||||
|
@ -17,25 +16,20 @@ module Oga
|
|||
# instances in threads just fine.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
class Lexer
|
||||
%% write data;
|
||||
|
||||
# % fix highlight
|
||||
|
||||
##
|
||||
# @param [String] data The data to lex.
|
||||
#
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
|
||||
##
|
||||
# Gathers all the tokens for the input and returns them as an Array.
|
||||
#
|
||||
# @see [#advance]
|
||||
# @return [Array]
|
||||
#
|
||||
def lex
|
||||
tokens = []
|
||||
|
||||
|
@ -46,7 +40,6 @@ module Oga
|
|||
return tokens
|
||||
end
|
||||
|
||||
##
|
||||
# Advances through the input and generates the corresponding tokens. Each
|
||||
# token is yielded to the supplied block.
|
||||
#
|
||||
|
@ -54,7 +47,6 @@ module Oga
|
|||
# the lexer loop has finished.
|
||||
#
|
||||
# @see [#add_token]
|
||||
#
|
||||
def advance(&block)
|
||||
@block = block
|
||||
|
||||
|
@ -88,7 +80,6 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Emits a token of which the value is based on the supplied start/stop
|
||||
# position.
|
||||
#
|
||||
|
@ -98,25 +89,21 @@ module Oga
|
|||
#
|
||||
# @see [#text]
|
||||
# @see [#add_token]
|
||||
#
|
||||
def emit(type, start, stop)
|
||||
value = slice_input(start, stop)
|
||||
|
||||
add_token(type, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the text between the specified start and stop position.
|
||||
#
|
||||
# @param [Fixnum] start
|
||||
# @param [Fixnum] stop
|
||||
# @return [String]
|
||||
#
|
||||
def slice_input(start, stop)
|
||||
return @data.byteslice(start, stop - start)
|
||||
end
|
||||
|
||||
##
|
||||
# Yields a new token to the supplied block.
|
||||
#
|
||||
# @param [Symbol] type The token type.
|
||||
|
@ -124,7 +111,6 @@ module Oga
|
|||
#
|
||||
# @yieldparam [Symbol] type
|
||||
# @yieldparam [String|NilClass] value
|
||||
#
|
||||
def add_token(type, value = nil)
|
||||
@block.call(type, value)
|
||||
end
|
||||
|
@ -202,7 +188,6 @@ module Oga
|
|||
#
|
||||
# Strings can be single or double quoted. They are mainly used for
|
||||
# attribute values.
|
||||
#
|
||||
dquote = '"';
|
||||
squote = "'";
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
%header
|
||||
{
|
||||
##
|
||||
# AST parser for CSS expressions.
|
||||
#
|
||||
# This parser does _not_ build a CSS specific AST, instead it directly produces
|
||||
|
@ -327,47 +326,35 @@ even
|
|||
|
||||
%inner
|
||||
{
|
||||
##
|
||||
# @return [Oga::LRU]
|
||||
#
|
||||
CACHE = LRU.new
|
||||
|
||||
##
|
||||
# @param [String] data
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def self.parse_with_cache(data)
|
||||
CACHE.get_or_set(data) { new(data).parse }
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] data The input to parse.
|
||||
#
|
||||
def initialize(data)
|
||||
@lexer = Lexer.new(data)
|
||||
end
|
||||
|
||||
##
|
||||
# Resets the internal state of the parser.
|
||||
#
|
||||
def reset
|
||||
@current_element = nil
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Symbol] type
|
||||
# @param [Array] children
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def s(type, *children)
|
||||
AST::Node.new(type, children)
|
||||
end
|
||||
|
||||
##
|
||||
# Yields the next token from the lexer.
|
||||
#
|
||||
# @yieldparam [Array]
|
||||
#
|
||||
def each_token
|
||||
@lexer.advance do |*args|
|
||||
yield args
|
||||
|
@ -376,16 +363,13 @@ even
|
|||
yield [-1, -1]
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the node test for the current element.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def current_element
|
||||
@current_element ||= s(:test, nil, '*')
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the input and returns the corresponding AST.
|
||||
#
|
||||
# @example
|
||||
|
@ -393,175 +377,140 @@ even
|
|||
# ast = parser.parse
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def parse
|
||||
reset
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for a node test.
|
||||
#
|
||||
# @param [String] namespace
|
||||
# @param [String] name
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_test(namespace, name)
|
||||
@current_element = s(:test, namespace, name)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class(name, arg = nil)
|
||||
handler = "on_pseudo_class_#{name.gsub('-', '_')}"
|
||||
|
||||
arg ? send(handler, arg) : send(handler)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `root` pseudo class.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_root
|
||||
s(:call, 'not', s(:axis, 'parent', s(:test, nil, '*')))
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `nth-child` pseudo class.
|
||||
#
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_nth_child(arg)
|
||||
generate_nth_child('preceding-sibling', arg)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `nth-last-child` pseudo class.
|
||||
#
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_nth_last_child(arg)
|
||||
generate_nth_child('following-sibling', arg)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `nth-of-type` pseudo class.
|
||||
#
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_nth_of_type(arg)
|
||||
generate_nth_child('preceding-sibling', arg, current_element)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `nth-last-of-type` pseudo class.
|
||||
#
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_nth_last_of_type(arg)
|
||||
generate_nth_child('following-sibling', arg, current_element)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `nth` pseudo class.
|
||||
#
|
||||
# @param [AST::Node] arg
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_nth(arg)
|
||||
s(:eq, s(:call, 'position'), arg)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:first-child` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_first_child
|
||||
generate_no_siblings('preceding-sibling')
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:last-child` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_last_child
|
||||
generate_no_siblings('following-sibling')
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:first-of-type` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_first_of_type
|
||||
generate_no_siblings('preceding-sibling', current_element)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:last-of-type` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_last_of_type
|
||||
generate_no_siblings('following-sibling', current_element)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:only-child` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_only_child
|
||||
s(:and, on_pseudo_class_first_child, on_pseudo_class_last_child)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:only-of-type` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_only_of_type
|
||||
s(:and, on_pseudo_class_first_of_type, on_pseudo_class_last_of_type)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `:empty` selector.
|
||||
#
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_pseudo_class_empty
|
||||
s(:call, 'not', s(:axis, 'child', s(:type_test, 'node')))
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_eq(attr, value)
|
||||
s(:eq, attr, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `~=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_space_in(attr, value)
|
||||
s(
|
||||
:call,
|
||||
|
@ -571,24 +520,20 @@ even
|
|||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `^=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_starts_with(attr, value)
|
||||
s(:call, 'starts-with', attr, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `$=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_ends_with(attr, value)
|
||||
s(
|
||||
:eq,
|
||||
|
@ -611,24 +556,20 @@ even
|
|||
)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `*=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_in(attr, value)
|
||||
s(:call, 'contains', attr, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the AST for the `|=` operator.
|
||||
#
|
||||
# @param [AST::Node] attr
|
||||
# @param [AST::Node] value
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def on_op_hyphen_in(attr, value)
|
||||
s(
|
||||
:or,
|
||||
|
@ -644,12 +585,10 @@ even
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# @param [String] count_axis
|
||||
# @param [AST::Node] arg
|
||||
# @param [AST::Node] count_test
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def generate_nth_child(count_axis, arg, count_test = s(:test, nil, '*'))
|
||||
count_call = s(:call, 'count', s(:axis, count_axis, count_test))
|
||||
|
||||
|
@ -679,43 +618,33 @@ even
|
|||
node
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] axis
|
||||
# @param [AST::Node] test
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def generate_no_siblings(axis, test = s(:test, nil, '*'))
|
||||
s(:eq, s(:call, 'count', s(:axis, axis, test)), s(:int, 0))
|
||||
end
|
||||
|
||||
##
|
||||
# @param [AST::Node] node
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def int_node?(node)
|
||||
node.type.equal?(:int)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [AST::Node] node
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def non_positive_number?(node)
|
||||
node.children[0] <= 0
|
||||
end
|
||||
|
||||
##
|
||||
# @param [AST::Node] node
|
||||
# @return [Symbol]
|
||||
#
|
||||
def step_comparison(node)
|
||||
node.children[0] >= 0 ? :gte : :lte
|
||||
end
|
||||
|
||||
##
|
||||
# @param [AST::Node] step
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def step_modulo_value(step)
|
||||
# -2n
|
||||
if step and non_positive_number?(step)
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
module Oga
|
||||
module EntityDecoder
|
||||
##
|
||||
# @see [decode]
|
||||
#
|
||||
def self.try_decode(input, html = false)
|
||||
input ? decode(input, html) : nil
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] input
|
||||
# @param [TrueClass|FalseClass] html
|
||||
# @return [String]
|
||||
#
|
||||
def self.decode(input, html = false)
|
||||
decoder = html ? HTML::Entities : XML::Entities
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
module Oga
|
||||
module HTML
|
||||
module Entities
|
||||
##
|
||||
# Hash mapping HTML entities to their Unicode character replacements.
|
||||
#
|
||||
# Based on the JSON output as listed at
|
||||
# http://www.w3.org/TR/html5/syntax.html#named-character-references
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
DECODE_MAPPING = {
|
||||
'Á' => [193].pack('U*'),
|
||||
'á' => [225].pack('U*'),
|
||||
|
@ -2137,11 +2135,9 @@ module Oga
|
|||
'‌' => [8204].pack('U*'),
|
||||
}
|
||||
|
||||
##
|
||||
# Decodes HTML entities.
|
||||
#
|
||||
# @see [decode]
|
||||
#
|
||||
def self.decode(input)
|
||||
XML::Entities.decode(input, DECODE_MAPPING)
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module HTML
|
||||
##
|
||||
# Parser for processing HTML input. This parser is a small wrapper around
|
||||
# {Oga::XML::Parser} and takes care of setting the various options required
|
||||
# for parsing HTML documents.
|
||||
|
@ -8,13 +7,10 @@ module Oga
|
|||
# A basic example:
|
||||
#
|
||||
# Oga::HTML::Parser.new('<meta charset="utf-8">').parse
|
||||
#
|
||||
class Parser < XML::Parser
|
||||
##
|
||||
# @param [String|IO] data
|
||||
# @param [Hash] options
|
||||
# @see [Oga::XML::Parser#initialize]
|
||||
#
|
||||
def initialize(data, options = {})
|
||||
options = options.merge(:html => true)
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
module Oga
|
||||
module HTML
|
||||
##
|
||||
# SAX parser for HTML documents. See the documentation of
|
||||
# {Oga::XML::SaxParser} for more information.
|
||||
#
|
||||
class SaxParser < XML::SaxParser
|
||||
##
|
||||
# @see [Oga::XML::SaxParser#initialize]
|
||||
#
|
||||
def initialize(handler, data, options = {})
|
||||
options = options.merge(:html => true)
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module Oga
|
||||
##
|
||||
# Thread-safe LRU cache using a Hash as the underlying storage engine.
|
||||
# Whenever the size of the cache exceeds the given limit the oldest keys are
|
||||
# removed (base on insert order).
|
||||
|
@ -22,11 +21,8 @@ module Oga
|
|||
# cache.keys # => [:b, :c, :d]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
class LRU
|
||||
##
|
||||
# @param [Fixnum] maximum
|
||||
#
|
||||
def initialize(maximum = 1024)
|
||||
@maximum = maximum
|
||||
@cache = {}
|
||||
|
@ -35,9 +31,7 @@ module Oga
|
|||
@owner = Thread.current
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Fixnum] value
|
||||
#
|
||||
def maximum=(value)
|
||||
synchronize do
|
||||
@maximum = value
|
||||
|
@ -46,30 +40,24 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Fixnum]
|
||||
#
|
||||
def maximum
|
||||
synchronize { @maximum }
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the value of the key.
|
||||
#
|
||||
# @param [Mixed] key
|
||||
# @return [Mixed]
|
||||
#
|
||||
def [](key)
|
||||
synchronize { @cache[key] }
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the key and its value. Old keys are discarded if the LRU size exceeds
|
||||
# the limit.
|
||||
#
|
||||
# @param [Mixed] key
|
||||
# @param [Mixed] value
|
||||
#
|
||||
def []=(key, value)
|
||||
synchronize do
|
||||
@cache[key] = value
|
||||
|
@ -82,35 +70,27 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a key if it exists, otherwise yields the supplied block and uses
|
||||
# its return value as the key value.
|
||||
#
|
||||
# @param [Mixed] key
|
||||
# @return [Mixed]
|
||||
#
|
||||
def get_or_set(key)
|
||||
synchronize { self[key] ||= yield }
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Array]
|
||||
#
|
||||
def keys
|
||||
synchronize { @keys }
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Mixed] key
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def key?(key)
|
||||
synchronize { @cache.key?(key) }
|
||||
end
|
||||
|
||||
##
|
||||
# Removes all keys from the cache.
|
||||
#
|
||||
def clear
|
||||
synchronize do
|
||||
@keys.clear
|
||||
|
@ -118,9 +98,7 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Fixnum]
|
||||
#
|
||||
def size
|
||||
synchronize { @cache.size }
|
||||
end
|
||||
|
@ -129,10 +107,8 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Yields the supplied block in a synchronized manner (if needed). This
|
||||
# method is heavily based on `MonitorMixin#mon_enter`.
|
||||
#
|
||||
def synchronize
|
||||
if @owner != Thread.current
|
||||
@mutex.synchronize do
|
||||
|
@ -147,10 +123,8 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Removes old keys until the size of the hash no longer exceeds the maximum
|
||||
# size.
|
||||
#
|
||||
def resize
|
||||
return unless size > @maximum
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
module Oga
|
||||
##
|
||||
# Parses the given XML document.
|
||||
#
|
||||
# @example
|
||||
|
@ -8,12 +7,10 @@ module Oga
|
|||
# @see [Oga::XML::Lexer#initialize]
|
||||
#
|
||||
# @return [Oga::XML::Document]
|
||||
#
|
||||
def self.parse_xml(xml, options = {})
|
||||
XML::Parser.new(xml, options).parse
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the given HTML document.
|
||||
#
|
||||
# @example
|
||||
|
@ -22,12 +19,10 @@ module Oga
|
|||
# @see [Oga::XML::Lexer#initialize]
|
||||
#
|
||||
# @return [Oga::XML::Document]
|
||||
#
|
||||
def self.parse_html(html, options = {})
|
||||
HTML::Parser.new(html, options).parse
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the given XML document using the SAX parser.
|
||||
#
|
||||
# @example
|
||||
|
@ -36,12 +31,10 @@ module Oga
|
|||
# Oga.sax_parse_html(handler, '<root>Hello</root>')
|
||||
#
|
||||
# @see [Oga::XML::SaxParser#initialize]
|
||||
#
|
||||
def self.sax_parse_xml(handler, xml, options = {})
|
||||
XML::SaxParser.new(handler, xml, options).parse
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the given HTML document using the SAX parser.
|
||||
#
|
||||
# @example
|
||||
|
@ -50,7 +43,6 @@ module Oga
|
|||
# Oga.sax_parse_html(handler, '<script>foo()</script>')
|
||||
#
|
||||
# @see [Oga::XML::SaxParser#initialize]
|
||||
#
|
||||
def self.sax_parse_html(handler, html, options = {})
|
||||
HTML::SaxParser.new(handler, html, options).parse
|
||||
end
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
module Oga
|
||||
module Ruby
|
||||
##
|
||||
# Class for converting a Ruby AST to a String.
|
||||
#
|
||||
# This class takes a {Oga::Ruby::Node} instance and converts it (and its
|
||||
# child nodes) to a String that in turn can be passed to `eval` and the
|
||||
# likes.
|
||||
#
|
||||
class Generator
|
||||
##
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def process(ast)
|
||||
send(:"on_#{ast.type}", ast)
|
||||
end
|
||||
|
@ -22,12 +18,10 @@ module Oga
|
|||
ast.to_a.map { |child| process(child) }.join("\n\n")
|
||||
end
|
||||
|
||||
##
|
||||
# Processes an assignment node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_assign(ast)
|
||||
var, val = *ast
|
||||
|
||||
|
@ -37,12 +31,10 @@ module Oga
|
|||
"#{var_str} = #{val_str}"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a mass assignment node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_massign(ast)
|
||||
vars, val = *ast
|
||||
|
||||
|
@ -52,12 +44,10 @@ module Oga
|
|||
"#{var_names.join(', ')} = #{val_str}"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a `begin` node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_begin(ast)
|
||||
body = process(ast.to_a[0])
|
||||
|
||||
|
@ -68,12 +58,10 @@ end
|
|||
EOF
|
||||
end
|
||||
|
||||
##
|
||||
# Processes an equality node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_eq(ast)
|
||||
left, right = *ast
|
||||
|
||||
|
@ -83,12 +71,10 @@ end
|
|||
"#{left_str} == #{right_str}"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a boolean "and" node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_and(ast)
|
||||
left, right = *ast
|
||||
|
||||
|
@ -98,12 +84,10 @@ end
|
|||
"#{left_str} && #{right_str}"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a boolean "or" node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_or(ast)
|
||||
left, right = *ast
|
||||
|
||||
|
@ -113,12 +97,10 @@ end
|
|||
"(#{left_str} || #{right_str})"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes an if statement node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_if(ast)
|
||||
cond, body, else_body = *ast
|
||||
|
||||
|
@ -144,12 +126,10 @@ end
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a while statement node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_while(ast)
|
||||
cond, body = *ast
|
||||
|
||||
|
@ -163,12 +143,10 @@ end
|
|||
EOF
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a method call node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_send(ast)
|
||||
receiver, name, *args = *ast
|
||||
|
||||
|
@ -188,12 +166,10 @@ end
|
|||
call
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a block node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_block(ast)
|
||||
receiver, args, body = *ast
|
||||
|
||||
|
@ -208,12 +184,10 @@ end
|
|||
EOF
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a Range node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_range(ast)
|
||||
start, stop = *ast
|
||||
|
||||
|
@ -223,32 +197,26 @@ end
|
|||
"(#{start_str}..#{stop_str})"
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a string node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_string(ast)
|
||||
ast.to_a[0].inspect
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a Symbol node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_symbol(ast)
|
||||
ast.to_a[0].to_sym.inspect
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a literal node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @return [String]
|
||||
#
|
||||
def on_lit(ast)
|
||||
ast.to_a[0]
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module Ruby
|
||||
##
|
||||
# Class representing a single node in a Ruby AST.
|
||||
#
|
||||
# The setup of this class is roughly based on the "ast" Gem. The "ast" Gem
|
||||
|
@ -25,7 +24,6 @@ module Oga
|
|||
# end
|
||||
#
|
||||
# @private
|
||||
#
|
||||
class Node < BasicObject
|
||||
undef_method :!, :!=
|
||||
|
||||
|
@ -46,16 +44,13 @@ module Oga
|
|||
|
||||
alias_method :to_ary, :to_a
|
||||
|
||||
##
|
||||
# Returns a "to_a" call node.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def to_array
|
||||
Node.new(:send, [self, :to_a])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an assignment node.
|
||||
#
|
||||
# This method wraps assigned values in a begin/end block to ensure that
|
||||
|
@ -63,7 +58,6 @@ module Oga
|
|||
#
|
||||
# @param [Oga::Ruby::Node] other
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def assign(other)
|
||||
if other.type == :followed_by
|
||||
other = other.wrap
|
||||
|
@ -72,133 +66,108 @@ module Oga
|
|||
Node.new(:assign, [self, other])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an equality expression node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] other
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def eq(other)
|
||||
Node.new(:eq, [self, other])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a boolean "and" node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] other
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def and(other)
|
||||
Node.new(:and, [self, other])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a boolean "or" node.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] other
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def or(other)
|
||||
Node.new(:or, [self, other])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a node that evaluates to its inverse.
|
||||
#
|
||||
# For example, a variable `foo` would be turned into `!foo`.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def not
|
||||
!self
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a node for Ruby's "is_a?" method.
|
||||
#
|
||||
# @param [Class] klass
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def is_a?(klass)
|
||||
Node.new(:send, [self, 'is_a?', Node.new(:lit, [klass.to_s])])
|
||||
end
|
||||
|
||||
##
|
||||
# Wraps the current node in a block.
|
||||
#
|
||||
# @param [Array] args Arguments (as Node instances) to pass to the block.
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def add_block(*args)
|
||||
Node.new(:block, [self, args, yield])
|
||||
end
|
||||
|
||||
##
|
||||
# Wraps the current node in a `begin` node.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def wrap
|
||||
Node.new(:begin, [self])
|
||||
end
|
||||
|
||||
##
|
||||
# Wraps the current node in an if statement node.
|
||||
#
|
||||
# The body of this statement is set to the return value of the supplied
|
||||
# block.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def if_true
|
||||
Node.new(:if, [self, yield])
|
||||
end
|
||||
|
||||
##
|
||||
# Wraps the current node in an `if !...` statement.
|
||||
#
|
||||
# @see [#if_true]
|
||||
#
|
||||
def if_false
|
||||
self.not.if_true { yield }
|
||||
end
|
||||
|
||||
##
|
||||
# Wraps the current node in a `while` statement.
|
||||
#
|
||||
# The body of this statement is set to the return value of the supplied
|
||||
# block.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def while_true
|
||||
Node.new(:while, [self, yield])
|
||||
end
|
||||
|
||||
##
|
||||
# Adds an "else" statement to the current node.
|
||||
#
|
||||
# This method assumes it's being called only on "if" nodes.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def else
|
||||
Node.new(:if, @children + [yield])
|
||||
end
|
||||
|
||||
##
|
||||
# Chains two nodes together.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] other
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def followed_by(other = nil)
|
||||
other = yield if ::Kernel.block_given?
|
||||
|
||||
Node.new(:followed_by, [self, other])
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a node for a method call.
|
||||
#
|
||||
# @param [Symbol] name The name of the method to call.
|
||||
|
@ -207,7 +176,6 @@ module Oga
|
|||
# method.
|
||||
#
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def method_missing(name, *args)
|
||||
Node.new(:send, [self, name.to_s, *args])
|
||||
end
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
module Oga
|
||||
##
|
||||
# @api private
|
||||
#
|
||||
class Whitelist < Blacklist
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def allow?(name)
|
||||
names.include?(name)
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Oga::Blacklist]
|
||||
#
|
||||
def to_blacklist
|
||||
Blacklist.new(names)
|
||||
end
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class for storing information about a single XML attribute.
|
||||
#
|
||||
class Attribute
|
||||
include ExpandedName
|
||||
|
||||
|
@ -19,25 +17,21 @@ module Oga
|
|||
|
||||
alias_method :parent, :element
|
||||
|
||||
##
|
||||
# The default namespace available to all attributes. This namespace can
|
||||
# not be modified.
|
||||
#
|
||||
# @return [Oga::XML::Namespace]
|
||||
#
|
||||
DEFAULT_NAMESPACE = Namespace.new(
|
||||
:name => 'xml',
|
||||
:uri => XML::DEFAULT_NAMESPACE.uri
|
||||
).freeze
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :name
|
||||
# @option options [String] :namespace_name
|
||||
# @option options [String] :value
|
||||
# @option options [Oga::XML::Element] :element
|
||||
#
|
||||
def initialize(options = {})
|
||||
@name = options[:name]
|
||||
@value = options[:value]
|
||||
|
@ -46,12 +40,10 @@ module Oga
|
|||
@namespace_name = options[:namespace_name]
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the {Oga::XML::Namespace} instance for the current namespace
|
||||
# name.
|
||||
#
|
||||
# @return [Oga::XML::Namespace]
|
||||
#
|
||||
def namespace
|
||||
unless @namespace
|
||||
if namespace_name == DEFAULT_NAMESPACE.name
|
||||
|
@ -64,19 +56,15 @@ module Oga
|
|||
@namespace
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] value
|
||||
#
|
||||
def value=(value)
|
||||
@value = value
|
||||
@decoded = false
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the value of the attribute or nil if no explicit value was set.
|
||||
#
|
||||
# @return [String|NilClass]
|
||||
#
|
||||
def value
|
||||
if !@decoded and @value
|
||||
@value = EntityDecoder.try_decode(@value, html?)
|
||||
|
@ -86,18 +74,14 @@ module Oga
|
|||
@value
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def text
|
||||
value.to_s
|
||||
end
|
||||
|
||||
alias_method :to_s, :text
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
if namespace_name
|
||||
full_name = "#{namespace_name}:#{name}"
|
||||
|
@ -110,9 +94,7 @@ module Oga
|
|||
%Q(#{full_name}="#{enc_value}")
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
segments = []
|
||||
|
||||
|
@ -138,9 +120,7 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html?
|
||||
!!@element && @element.html?
|
||||
end
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class used for storing information about CDATA tags.
|
||||
#
|
||||
class Cdata < CharacterNode
|
||||
##
|
||||
# Converts the node back to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
"<![CDATA[#{text}]]>"
|
||||
end
|
||||
|
|
|
@ -1,34 +1,26 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Base class for nodes that represent a text-like value such as Text and
|
||||
# Comment nodes.
|
||||
#
|
||||
class CharacterNode < Node
|
||||
# @return [String]
|
||||
attr_accessor :text
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :text The text of the node.
|
||||
#
|
||||
def initialize(options = {})
|
||||
super
|
||||
|
||||
@text = options[:text]
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
text.to_s
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
"#{self.class.to_s.split('::').last}(#{text.inspect})"
|
||||
end
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class used for storing information about XML comments.
|
||||
#
|
||||
class Comment < CharacterNode
|
||||
##
|
||||
# Converts the node back to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
"<!--#{text}-->"
|
||||
end
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The default XML namespace.
|
||||
#
|
||||
# @return [Oga::XML::Namespace]
|
||||
#
|
||||
DEFAULT_NAMESPACE = Namespace.new(
|
||||
:name => 'xmlns',
|
||||
:uri => 'http://www.w3.org/XML/1998/namespace'
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class used for storing information about Doctypes.
|
||||
#
|
||||
class Doctype
|
||||
# The name of the doctype (e.g. "HTML").
|
||||
# @return [String]
|
||||
|
@ -24,7 +22,6 @@ module Oga
|
|||
# @return [String]
|
||||
attr_accessor :inline_rules
|
||||
|
||||
##
|
||||
# @example
|
||||
# dtd = Doctype.new(:name => 'html', :type => 'PUBLIC')
|
||||
#
|
||||
|
@ -34,7 +31,6 @@ module Oga
|
|||
# @option options [String] :type
|
||||
# @option options [String] :public_id
|
||||
# @option options [String] :system_id
|
||||
#
|
||||
def initialize(options = {})
|
||||
@name = options[:name]
|
||||
@type = options[:type]
|
||||
|
@ -43,11 +39,9 @@ module Oga
|
|||
@inline_rules = options[:inline_rules]
|
||||
end
|
||||
|
||||
##
|
||||
# Converts the doctype back to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
segments = "<!DOCTYPE #{name}"
|
||||
|
||||
|
@ -59,11 +53,9 @@ module Oga
|
|||
segments + '>'
|
||||
end
|
||||
|
||||
##
|
||||
# Inspects the doctype.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
segments = []
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class used for storing information about an entire XML document. This
|
||||
# includes the doctype, XML declaration, child nodes and more.
|
||||
#
|
||||
class Document
|
||||
include Querying
|
||||
include Traversal
|
||||
|
@ -18,14 +16,12 @@ module Oga
|
|||
# @return [Symbol]
|
||||
attr_reader :type
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [Oga::XML::NodeSet] :children
|
||||
# @option options [Oga::XML::Doctype] :doctype
|
||||
# @option options [Oga::XML::XmlDeclaration] :xml_declaration
|
||||
# @option options [Symbol] :type
|
||||
#
|
||||
def initialize(options = {})
|
||||
@doctype = options[:doctype]
|
||||
@xml_declaration = options[:xml_declaration]
|
||||
|
@ -34,18 +30,14 @@ module Oga
|
|||
self.children = options[:children] if options[:children]
|
||||
end
|
||||
|
||||
##
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def children
|
||||
@children ||= NodeSet.new([], self)
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the child nodes of the document.
|
||||
#
|
||||
# @param [Oga::XML::NodeSet|Array] nodes
|
||||
#
|
||||
def children=(nodes)
|
||||
if nodes.is_a?(NodeSet)
|
||||
@children = nodes
|
||||
|
@ -54,23 +46,19 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns self.
|
||||
#
|
||||
# This method exists to make this class compatible with Element, which in
|
||||
# turn makes it easier to use both in the XPath compiler.
|
||||
#
|
||||
# @return [Oga::XML::Document]
|
||||
#
|
||||
def root_node
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Converts the document and its child nodes to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
xml = children.map(&:to_xml).join('')
|
||||
|
||||
|
@ -85,19 +73,15 @@ module Oga
|
|||
xml
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html?
|
||||
type.equal?(:html)
|
||||
end
|
||||
|
||||
##
|
||||
# Inspects the document and its child nodes. Child nodes are indented for
|
||||
# each nesting level.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
segments = []
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class that contains information about an XML element such as the name,
|
||||
# attributes and child nodes.
|
||||
#
|
||||
class Element < Node
|
||||
include Querying
|
||||
include ExpandedName
|
||||
|
@ -20,14 +18,11 @@ module Oga
|
|||
# @return [Hash]
|
||||
attr_writer :namespaces
|
||||
|
||||
##
|
||||
# The attribute prefix/namespace used for registering element namespaces.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
XMLNS_PREFIX = 'xmlns'.freeze
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :name The name of the element.
|
||||
|
@ -36,7 +31,6 @@ module Oga
|
|||
#
|
||||
# @option options [Array<Oga::XML::Attribute>] :attributes The attributes
|
||||
# of the element as an Array.
|
||||
#
|
||||
def initialize(options = {})
|
||||
super
|
||||
|
||||
|
@ -49,15 +43,12 @@ module Oga
|
|||
register_namespaces_from_attributes
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
#
|
||||
def namespace_name=(name)
|
||||
@namespace_name = name
|
||||
@namespace = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an attribute matching the given name (with or without the
|
||||
# namespace).
|
||||
#
|
||||
|
@ -72,7 +63,6 @@ module Oga
|
|||
# of the attribute.
|
||||
#
|
||||
# @return [Oga::XML::Attribute]
|
||||
#
|
||||
def attribute(name)
|
||||
name, ns = split_name(name)
|
||||
|
||||
|
@ -85,32 +75,27 @@ module Oga
|
|||
|
||||
alias_method :attr, :attribute
|
||||
|
||||
##
|
||||
# Returns the value of the given attribute.
|
||||
#
|
||||
# @example
|
||||
# element.get('class') # => "container"
|
||||
#
|
||||
# @see [#attribute]
|
||||
#
|
||||
def get(name)
|
||||
found = attribute(name)
|
||||
|
||||
found ? found.value : nil
|
||||
end
|
||||
|
||||
##
|
||||
# Adds a new attribute to the element.
|
||||
#
|
||||
# @param [Oga::XML::Attribute] attribute
|
||||
#
|
||||
def add_attribute(attribute)
|
||||
attribute.element = self
|
||||
|
||||
attributes << attribute
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the value of an attribute to the given value. If the attribute does
|
||||
# not exist it is created automatically.
|
||||
#
|
||||
|
@ -118,7 +103,6 @@ module Oga
|
|||
# namespace.
|
||||
#
|
||||
# @param [String] value The new value of the attribute.
|
||||
#
|
||||
def set(name, value)
|
||||
found = attribute(name)
|
||||
|
||||
|
@ -141,25 +125,21 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Removes an attribute from the element.
|
||||
#
|
||||
# @param [String] name The name (optionally including namespace prefix)
|
||||
# of the attribute to remove.
|
||||
#
|
||||
# @return [Oga::XML::Attribute]
|
||||
#
|
||||
def unset(name)
|
||||
found = attribute(name)
|
||||
|
||||
return attributes.delete(found) if found
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the namespace of the element.
|
||||
#
|
||||
# @return [Oga::XML::Namespace]
|
||||
#
|
||||
def namespace
|
||||
unless @namespace
|
||||
available = available_namespaces
|
||||
|
@ -169,40 +149,32 @@ module Oga
|
|||
@namespace
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the namespaces registered on this element, or an empty Hash in
|
||||
# case of an HTML element.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
def namespaces
|
||||
html? ? {} : @namespaces
|
||||
end
|
||||
|
||||
##
|
||||
# Returns true if the current element resides in the default XML
|
||||
# namespace.
|
||||
#
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def default_namespace?
|
||||
namespace == DEFAULT_NAMESPACE || namespace.nil?
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the text of all child nodes joined together.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def text
|
||||
children.text
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the text of the current element only.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def inner_text
|
||||
text = ''
|
||||
|
||||
|
@ -213,12 +185,10 @@ module Oga
|
|||
text
|
||||
end
|
||||
|
||||
##
|
||||
# Returns any {Oga::XML::Text} nodes that are a direct child of this
|
||||
# element.
|
||||
#
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def text_nodes
|
||||
nodes = NodeSet.new
|
||||
|
||||
|
@ -229,21 +199,17 @@ module Oga
|
|||
nodes
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the inner text of the current element to the given String.
|
||||
#
|
||||
# @param [String] text
|
||||
#
|
||||
def inner_text=(text)
|
||||
text_node = XML::Text.new(:text => text)
|
||||
@children = NodeSet.new([text_node], self)
|
||||
end
|
||||
|
||||
##
|
||||
# Converts the element and its child elements to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
if namespace_name
|
||||
full_name = "#{namespace_name}:#{name}"
|
||||
|
@ -265,9 +231,7 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
segments = []
|
||||
|
||||
|
@ -284,7 +248,6 @@ module Oga
|
|||
"Element(#{segments.join(' ')})"
|
||||
end
|
||||
|
||||
##
|
||||
# Registers a new namespace for the current element and its child
|
||||
# elements.
|
||||
#
|
||||
|
@ -292,7 +255,6 @@ module Oga
|
|||
# @param [String] uri
|
||||
# @param [TrueClass|FalseClass] flush
|
||||
# @see [Oga::XML::Namespace#initialize]
|
||||
#
|
||||
def register_namespace(name, uri, flush = true)
|
||||
if namespaces[name]
|
||||
raise ArgumentError, "The namespace #{name.inspect} already exists"
|
||||
|
@ -303,12 +265,10 @@ module Oga
|
|||
flush_namespaces_cache if flush
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a Hash containing all the namespaces available to the current
|
||||
# element.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
def available_namespaces
|
||||
# HTML(5) completely ignores namespaces
|
||||
unless @available_namespaces
|
||||
|
@ -333,11 +293,9 @@ module Oga
|
|||
@available_namespaces
|
||||
end
|
||||
|
||||
##
|
||||
# Returns `true` if the element is a self-closing element.
|
||||
#
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def self_closing?
|
||||
self_closing = children.empty?
|
||||
root = root_node
|
||||
|
@ -350,10 +308,8 @@ module Oga
|
|||
self_closing
|
||||
end
|
||||
|
||||
##
|
||||
# Flushes the namespaces cache of the current element and all its child
|
||||
# elements.
|
||||
#
|
||||
def flush_namespaces_cache
|
||||
@available_namespaces = nil
|
||||
@namespace = nil
|
||||
|
@ -365,9 +321,7 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Registers namespaces based on any "xmlns" attributes.
|
||||
#
|
||||
def register_namespaces_from_attributes
|
||||
flush = false
|
||||
|
||||
|
@ -386,31 +340,25 @@ module Oga
|
|||
flush_namespaces_cache if flush
|
||||
end
|
||||
|
||||
##
|
||||
# Links all attributes to the current element.
|
||||
#
|
||||
def link_attributes
|
||||
attributes.each do |attr|
|
||||
attr.element = self
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
# @return [Array]
|
||||
#
|
||||
def split_name(name)
|
||||
segments = name.to_s.split(':')
|
||||
|
||||
[segments.pop, segments.pop]
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::XML::Attribute] attr
|
||||
# @param [String] ns
|
||||
# @param [String] name
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def attribute_matches?(attr, ns, name)
|
||||
name_matches = attr.name == name
|
||||
ns_matches = false
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Module for encoding/decoding XML and HTML entities. The mapping of HTML
|
||||
# entities can be found in {Oga::HTML::Entities::DECODE_MAPPING}.
|
||||
#
|
||||
module Entities
|
||||
##
|
||||
# Hash containing XML entities and the corresponding characters.
|
||||
#
|
||||
# The `&` mapping must come last to ensure proper conversion of non
|
||||
# encoded to encoded forms (see {Oga::XML::Text#to_xml}).
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
DECODE_MAPPING = {
|
||||
'<' => '<',
|
||||
'>' => '>',
|
||||
|
@ -21,23 +17,19 @@ module Oga
|
|||
'&' => '&',
|
||||
}
|
||||
|
||||
##
|
||||
# Hash containing characters and the corresponding XML entities.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
ENCODE_MAPPING = {
|
||||
'&' => '&',
|
||||
'>' => '>',
|
||||
'<' => '<',
|
||||
}
|
||||
|
||||
##
|
||||
# Hash containing characters and the corresponding XML entities to use
|
||||
# when encoding XML/HTML attribute values.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
ENCODE_ATTRIBUTE_MAPPING = {
|
||||
'&' => '&',
|
||||
'>' => '>',
|
||||
|
@ -46,50 +38,36 @@ module Oga
|
|||
'"' => '"'
|
||||
}
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
AMPERSAND = '&'.freeze
|
||||
|
||||
##
|
||||
# Regexp for matching XML/HTML entities such as " ".
|
||||
#
|
||||
# @return [Regexp]
|
||||
#
|
||||
REGULAR_ENTITY = /&[a-zA-Z0-9]+;/
|
||||
|
||||
##
|
||||
# Regexp for matching XML/HTML numeric entities such as "&".
|
||||
#
|
||||
# @return [Regexp]
|
||||
#
|
||||
NUMERIC_CODE_POINT_ENTITY = /&#(\d+);/
|
||||
|
||||
##
|
||||
# Regexp for matching XML/HTML hex entities such as "<".
|
||||
#
|
||||
# @return [Regexp]
|
||||
#
|
||||
HEX_CODE_POINT_ENTITY = /&#x([a-fA-F0-9]+);/
|
||||
|
||||
##
|
||||
# @return [Regexp]
|
||||
#
|
||||
ENCODE_REGEXP = Regexp.new(ENCODE_MAPPING.keys.join('|'))
|
||||
|
||||
##
|
||||
# @return [Regexp]
|
||||
#
|
||||
ENCODE_ATTRIBUTE_REGEXP =
|
||||
Regexp.new(ENCODE_ATTRIBUTE_MAPPING.keys.join('|'))
|
||||
|
||||
##
|
||||
# Decodes XML entities.
|
||||
#
|
||||
# @param [String] input
|
||||
# @param [Hash] mapping
|
||||
# @return [String]
|
||||
#
|
||||
def self.decode(input, mapping = DECODE_MAPPING)
|
||||
return input unless input.include?(AMPERSAND)
|
||||
|
||||
|
@ -110,23 +88,19 @@ module Oga
|
|||
input
|
||||
end
|
||||
|
||||
##
|
||||
# Encodes special characters as XML entities.
|
||||
#
|
||||
# @param [String] input
|
||||
# @param [Hash] mapping
|
||||
# @return [String]
|
||||
#
|
||||
def self.encode(input, mapping = ENCODE_MAPPING)
|
||||
input.gsub(ENCODE_REGEXP, mapping)
|
||||
end
|
||||
|
||||
##
|
||||
# Encodes special characters in an XML attribute value.
|
||||
#
|
||||
# @param [String] input
|
||||
# @return [String]
|
||||
#
|
||||
def self.encode_attribute(input)
|
||||
input.gsub(ENCODE_ATTRIBUTE_REGEXP, ENCODE_ATTRIBUTE_MAPPING)
|
||||
end
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
module Oga
|
||||
module XML
|
||||
module ExpandedName
|
||||
##
|
||||
# Returns the expanded name of the current Element or Attribute.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def expanded_name
|
||||
namespace_name ? "#{namespace_name}:#{name}" : name
|
||||
end
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Names of the HTML void elements that should be handled when HTML lexing
|
||||
# is enabled.
|
||||
#
|
||||
# @api private
|
||||
# @return [Oga::Whitelist]
|
||||
#
|
||||
HTML_VOID_ELEMENTS = Whitelist.new(%w{
|
||||
area base br col command embed hr img input keygen link meta param source
|
||||
track wbr
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Low level lexer that supports both XML and HTML (using an extra option).
|
||||
# To lex HTML input set the `:html` option to `true` when creating an
|
||||
# instance of the lexer:
|
||||
|
@ -46,7 +45,6 @@ module Oga
|
|||
# Strict mode only applies to XML documents.
|
||||
#
|
||||
# @private
|
||||
#
|
||||
class Lexer
|
||||
# These are all constant/frozen to remove the need for String allocations
|
||||
# every time they are referenced in the lexer.
|
||||
|
@ -96,12 +94,9 @@ module Oga
|
|||
HTML_CLOSE_SELF[key.upcase] = HTML_CLOSE_SELF[key]
|
||||
end
|
||||
|
||||
##
|
||||
# Names of HTML tags of which the content should be lexed as-is.
|
||||
#
|
||||
LITERAL_HTML_ELEMENTS = Whitelist.new([HTML_SCRIPT, HTML_STYLE])
|
||||
|
||||
##
|
||||
# @param [String|IO] data The data to lex. This can either be a String or
|
||||
# an IO instance.
|
||||
#
|
||||
|
@ -113,7 +108,6 @@ module Oga
|
|||
#
|
||||
# @option options [TrueClass|FalseClass] :strict Enables/disables strict
|
||||
# parsing of XML documents, disabled by default.
|
||||
#
|
||||
def initialize(data, options = {})
|
||||
@data = data
|
||||
@html = options[:html]
|
||||
|
@ -122,11 +116,9 @@ module Oga
|
|||
reset
|
||||
end
|
||||
|
||||
##
|
||||
# Resets the internal state of the lexer. Typically you don't need to
|
||||
# call this method yourself as its called by #lex after lexing a given
|
||||
# String.
|
||||
#
|
||||
def reset
|
||||
@line = 1
|
||||
@elements = []
|
||||
|
@ -136,12 +128,10 @@ module Oga
|
|||
reset_native
|
||||
end
|
||||
|
||||
##
|
||||
# Yields the data to lex to the supplied block.
|
||||
#
|
||||
# @return [String]
|
||||
# @yieldparam [String]
|
||||
#
|
||||
def read_data
|
||||
if @data.is_a?(String)
|
||||
yield @data
|
||||
|
@ -157,7 +147,6 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Gathers all the tokens for the input and returns them as an Array.
|
||||
#
|
||||
# This method resets the internal state of the lexer after consuming the
|
||||
|
@ -165,7 +154,6 @@ module Oga
|
|||
#
|
||||
# @see #advance
|
||||
# @return [Array]
|
||||
#
|
||||
def lex
|
||||
tokens = []
|
||||
|
||||
|
@ -178,7 +166,6 @@ module Oga
|
|||
tokens
|
||||
end
|
||||
|
||||
##
|
||||
# Advances through the input and generates the corresponding tokens. Each
|
||||
# token is yielded to the supplied block.
|
||||
#
|
||||
|
@ -196,7 +183,6 @@ module Oga
|
|||
# @yieldparam [Symbol] type
|
||||
# @yieldparam [String] value
|
||||
# @yieldparam [Fixnum] line
|
||||
#
|
||||
def advance(&block)
|
||||
@block = block
|
||||
|
||||
|
@ -212,44 +198,33 @@ module Oga
|
|||
@block = nil
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html?
|
||||
@html == true
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def strict?
|
||||
@strict
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html_script?
|
||||
html? && current_element == HTML_SCRIPT
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html_style?
|
||||
html? && current_element == HTML_STYLE
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# @param [Fixnum] amount The amount of lines to advance.
|
||||
#
|
||||
def advance_line(amount = 1)
|
||||
@line += amount
|
||||
end
|
||||
|
||||
##
|
||||
# Calls the supplied block with the information of the current token.
|
||||
#
|
||||
# @param [Symbol] type The token type.
|
||||
|
@ -258,192 +233,145 @@ module Oga
|
|||
# @yieldparam [String] type
|
||||
# @yieldparam [String] value
|
||||
# @yieldparam [Fixnum] line
|
||||
#
|
||||
def add_token(type, value = nil)
|
||||
@block.call(type, value, @line)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the name of the element we're currently in.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def current_element
|
||||
@elements.last
|
||||
end
|
||||
|
||||
##
|
||||
# Called when processing a single quote.
|
||||
#
|
||||
def on_string_squote
|
||||
add_token(:T_STRING_SQUOTE)
|
||||
end
|
||||
|
||||
##
|
||||
# Called when processing a double quote.
|
||||
#
|
||||
def on_string_dquote
|
||||
add_token(:T_STRING_DQUOTE)
|
||||
end
|
||||
|
||||
##
|
||||
# Called when processing the body of a string.
|
||||
#
|
||||
# @param [String] value The data between the quotes.
|
||||
#
|
||||
def on_string_body(value)
|
||||
add_token(:T_STRING_BODY, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called when a doctype starts.
|
||||
#
|
||||
def on_doctype_start
|
||||
add_token(:T_DOCTYPE_START)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the identifier specifying the type of the doctype.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_doctype_type(value)
|
||||
add_token(:T_DOCTYPE_TYPE, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the identifier specifying the name of the doctype.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_doctype_name(value)
|
||||
add_token(:T_DOCTYPE_NAME, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the end of a doctype.
|
||||
#
|
||||
def on_doctype_end
|
||||
add_token(:T_DOCTYPE_END)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on an inline doctype block.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_doctype_inline(value)
|
||||
add_token(:T_DOCTYPE_INLINE, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the open CDATA tag.
|
||||
#
|
||||
def on_cdata_start
|
||||
add_token(:T_CDATA_START)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the closing CDATA tag.
|
||||
#
|
||||
def on_cdata_end
|
||||
add_token(:T_CDATA_END)
|
||||
end
|
||||
|
||||
##
|
||||
# Called for the body of a CDATA tag.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_cdata_body(value)
|
||||
add_token(:T_CDATA_BODY, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the open comment tag.
|
||||
#
|
||||
def on_comment_start
|
||||
add_token(:T_COMMENT_START)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the closing comment tag.
|
||||
#
|
||||
def on_comment_end
|
||||
add_token(:T_COMMENT_END)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on a comment.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_comment_body(value)
|
||||
add_token(:T_COMMENT_BODY, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the start of an XML declaration tag.
|
||||
#
|
||||
def on_xml_decl_start
|
||||
add_token(:T_XML_DECL_START)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the end of an XML declaration tag.
|
||||
#
|
||||
def on_xml_decl_end
|
||||
add_token(:T_XML_DECL_END)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the start of a processing instruction.
|
||||
#
|
||||
def on_proc_ins_start
|
||||
add_token(:T_PROC_INS_START)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on a processing instruction name.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_proc_ins_name(value)
|
||||
add_token(:T_PROC_INS_NAME, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the body of a processing instruction.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_proc_ins_body(value)
|
||||
add_token(:T_PROC_INS_BODY, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the end of a processing instruction.
|
||||
#
|
||||
def on_proc_ins_end
|
||||
add_token(:T_PROC_INS_END)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the name of an element.
|
||||
#
|
||||
# @param [String] name The name of the element, including namespace.
|
||||
#
|
||||
def on_element_name(name)
|
||||
before_html_element_name(name) if html?
|
||||
|
||||
add_element(name)
|
||||
end
|
||||
|
||||
##
|
||||
# Handles inserting of any missing tags whenever a new HTML tag is opened.
|
||||
#
|
||||
# @param [String] name
|
||||
#
|
||||
def before_html_element_name(name)
|
||||
close_current = HTML_CLOSE_SELF[current_element]
|
||||
|
||||
|
@ -463,27 +391,21 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
#
|
||||
def add_element(name)
|
||||
@elements << name
|
||||
|
||||
add_token(:T_ELEM_NAME, name)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the element namespace.
|
||||
#
|
||||
# @param [String] namespace
|
||||
#
|
||||
def on_element_ns(namespace)
|
||||
add_token(:T_ELEM_NS, namespace)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the closing `>` of the open tag of an element.
|
||||
#
|
||||
def on_element_open_end
|
||||
return unless html?
|
||||
|
||||
|
@ -496,12 +418,10 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Called on the closing tag of an element.
|
||||
#
|
||||
# @param [String] name The name of the element (minus namespace
|
||||
# prefix). This is not set for self closing tags.
|
||||
#
|
||||
def on_element_end(name = nil)
|
||||
return if @elements.empty?
|
||||
|
||||
|
@ -520,31 +440,25 @@ module Oga
|
|||
@elements.pop
|
||||
end
|
||||
|
||||
##
|
||||
# Called on regular text values.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_text(value)
|
||||
return if value.empty?
|
||||
|
||||
add_token(:T_TEXT, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on attribute namespaces.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_attribute_ns(value)
|
||||
add_token(:T_ATTR_NS, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Called on tag attributes.
|
||||
#
|
||||
# @param [String] value
|
||||
#
|
||||
def on_attribute(value)
|
||||
add_token(:T_ATTR, value)
|
||||
end
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The Namespace class contains information about XML namespaces such as the
|
||||
# name and URI.
|
||||
#
|
||||
class Namespace
|
||||
# @return [String]
|
||||
attr_accessor :name
|
||||
|
@ -11,35 +9,27 @@ module Oga
|
|||
# @return [String]
|
||||
attr_accessor :uri
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :name
|
||||
# @option options [String] :uri
|
||||
#
|
||||
def initialize(options = {})
|
||||
@name = options[:name]
|
||||
@uri = options[:uri]
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def to_s
|
||||
name.to_s
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
"Namespace(name: #{name.inspect} uri: #{uri.inspect})"
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::XML::Namespace] other
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def ==(other)
|
||||
other.is_a?(self.class) && name == other.name && uri == other.uri
|
||||
end
|
||||
|
|
|
@ -1,17 +1,14 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# A generic XML node. Instances of this class can belong to a
|
||||
# {Oga::XML::NodeSet} and can be used to query surrounding and parent
|
||||
# nodes.
|
||||
#
|
||||
class Node
|
||||
include Traversal
|
||||
|
||||
# @return [Oga::XML::NodeSet]
|
||||
attr_reader :node_set
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [Oga::XML::NodeSet] :node_set The node set that this
|
||||
|
@ -19,35 +16,28 @@ module Oga
|
|||
#
|
||||
# @option options [Oga::XML::NodeSet|Array] :children The child nodes of
|
||||
# the current node.
|
||||
#
|
||||
def initialize(options = {})
|
||||
self.node_set = options[:node_set]
|
||||
self.children = options[:children] if options[:children]
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::XML::NodeSet] set
|
||||
#
|
||||
def node_set=(set)
|
||||
@node_set = set
|
||||
@root_node = nil
|
||||
@html_p = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the child nodes of the current node.
|
||||
#
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def children
|
||||
@children ||= NodeSet.new([], self)
|
||||
end
|
||||
|
||||
##
|
||||
# Sets the child nodes of the element.
|
||||
#
|
||||
# @param [Oga::XML::NodeSet|Array] nodes
|
||||
#
|
||||
def children=(nodes)
|
||||
if nodes.is_a?(NodeSet)
|
||||
@children = nodes
|
||||
|
@ -56,32 +46,26 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the parent node of the current node. If there is no parent node
|
||||
# `nil` is returned instead.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def parent
|
||||
node_set ? node_set.owner : nil
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the preceding node, or nil if there is none.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def previous
|
||||
index = node_set.index(self) - 1
|
||||
|
||||
index >= 0 ? node_set[index] : nil
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the following node, or nil if there is none.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def next
|
||||
index = node_set.index(self) + 1
|
||||
length = node_set.length
|
||||
|
@ -89,11 +73,9 @@ module Oga
|
|||
index <= length ? node_set[index] : nil
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the previous element node or nil if there is none.
|
||||
#
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def previous_element
|
||||
node = self
|
||||
|
||||
|
@ -104,11 +86,9 @@ module Oga
|
|||
return
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the next element node or nil if there is none.
|
||||
#
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def next_element
|
||||
node = self
|
||||
|
||||
|
@ -119,12 +99,10 @@ module Oga
|
|||
return
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the root document/node of the current node. The node is
|
||||
# retrieved by traversing upwards in the DOM tree from the current node.
|
||||
#
|
||||
# @return [Oga::XML::Document|Oga::XML::Node]
|
||||
#
|
||||
def root_node
|
||||
unless @root_node
|
||||
node = self
|
||||
|
@ -143,16 +121,13 @@ module Oga
|
|||
@root_node
|
||||
end
|
||||
|
||||
##
|
||||
# Removes the current node from the owning node set.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def remove
|
||||
return node_set.delete(self) if node_set
|
||||
end
|
||||
|
||||
##
|
||||
# Replaces the current node with another.
|
||||
#
|
||||
# @example Replacing with an element
|
||||
|
@ -163,7 +138,6 @@ module Oga
|
|||
# some_node.replace('this will replace the current node with a text node')
|
||||
#
|
||||
# @param [String|Oga::XML::Node] other
|
||||
#
|
||||
def replace(other)
|
||||
if other.is_a?(String)
|
||||
other = Text.new(:text => other)
|
||||
|
@ -173,31 +147,25 @@ module Oga
|
|||
remove
|
||||
end
|
||||
|
||||
##
|
||||
# Inserts the given node before the current node.
|
||||
#
|
||||
# @param [Oga::XML::Node] other
|
||||
#
|
||||
def before(other)
|
||||
index = node_set.index(self)
|
||||
|
||||
node_set.insert(index, other)
|
||||
end
|
||||
|
||||
##
|
||||
# Inserts the given node after the current node.
|
||||
#
|
||||
# @param [Oga::XML::Node] other
|
||||
#
|
||||
def after(other)
|
||||
index = node_set.index(self) + 1
|
||||
|
||||
node_set.insert(index, other)
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def html?
|
||||
if @html_p.nil?
|
||||
root = root_node
|
||||
|
@ -208,14 +176,11 @@ module Oga
|
|||
@html_p
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def xml?
|
||||
!html?
|
||||
end
|
||||
|
||||
##
|
||||
# Yields all ancestor elements of the current node.
|
||||
#
|
||||
# @example
|
||||
|
@ -224,7 +189,6 @@ module Oga
|
|||
# end
|
||||
#
|
||||
# @yieldparam [Oga::XML::Node]
|
||||
#
|
||||
def each_ancestor
|
||||
node = parent
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The NodeSet class contains a set of unique {Oga::XML::Node} instances that
|
||||
# can be queried and modified. Optionally NodeSet instances can take
|
||||
# ownership of a node (besides just containing it). This allows the nodes to
|
||||
|
@ -30,17 +29,14 @@ module Oga
|
|||
#
|
||||
# If ownership was not handled then you'd have to manually set the
|
||||
# `element` variable's `node_set` attribute after pushing it into a set.
|
||||
#
|
||||
class NodeSet
|
||||
include Enumerable
|
||||
|
||||
# @return [Oga::XML::Node]
|
||||
attr_accessor :owner
|
||||
|
||||
##
|
||||
# @param [Array] nodes The nodes to add to the set.
|
||||
# @param [Oga::XML::NodeSet] owner The owner of the set.
|
||||
#
|
||||
def initialize(nodes = [], owner = nil)
|
||||
@nodes = nodes
|
||||
@owner = owner
|
||||
|
@ -53,38 +49,30 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Yields the supplied block for every node.
|
||||
#
|
||||
# @yieldparam [Oga::XML::Node]
|
||||
#
|
||||
def each
|
||||
@nodes.each { |node| yield node }
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the last node in the set.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def last
|
||||
@nodes[-1]
|
||||
end
|
||||
|
||||
##
|
||||
# Returns `true` if the set is empty.
|
||||
#
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def empty?
|
||||
@nodes.empty?
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the amount of nodes in the set.
|
||||
#
|
||||
# @return [Fixnum]
|
||||
#
|
||||
def length
|
||||
@nodes.length
|
||||
end
|
||||
|
@ -92,21 +80,17 @@ module Oga
|
|||
alias_method :count, :length
|
||||
alias_method :size, :length
|
||||
|
||||
##
|
||||
# Returns the index of the given node.
|
||||
#
|
||||
# @param [Oga::XML::Node] node
|
||||
# @return [Fixnum]
|
||||
#
|
||||
def index(node)
|
||||
@nodes.index(node)
|
||||
end
|
||||
|
||||
##
|
||||
# Pushes the node at the end of the set.
|
||||
#
|
||||
# @param [Oga::XML::Node] node
|
||||
#
|
||||
def push(node)
|
||||
return if exists?(node)
|
||||
|
||||
|
@ -119,11 +103,9 @@ module Oga
|
|||
|
||||
alias_method :<<, :push
|
||||
|
||||
##
|
||||
# Pushes the node at the start of the set.
|
||||
#
|
||||
# @param [Oga::XML::Node] node
|
||||
#
|
||||
def unshift(node)
|
||||
return if exists?(node)
|
||||
|
||||
|
@ -134,11 +116,9 @@ module Oga
|
|||
take_ownership(node) if @owner
|
||||
end
|
||||
|
||||
##
|
||||
# Shifts a node from the start of the set.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def shift
|
||||
node = @nodes.shift
|
||||
|
||||
|
@ -151,11 +131,9 @@ module Oga
|
|||
node
|
||||
end
|
||||
|
||||
##
|
||||
# Pops a node from the end of the set.
|
||||
#
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def pop
|
||||
node = @nodes.pop
|
||||
|
||||
|
@ -168,12 +146,10 @@ module Oga
|
|||
node
|
||||
end
|
||||
|
||||
##
|
||||
# Inserts a node into the set at the given index.
|
||||
#
|
||||
# @param [Fixnum] index The index to insert the node at.
|
||||
# @param [Oga::XML::Node] node
|
||||
#
|
||||
def insert(index, node)
|
||||
return if exists?(node)
|
||||
|
||||
|
@ -184,73 +160,59 @@ module Oga
|
|||
take_ownership(node) if @owner
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the node for the given index.
|
||||
#
|
||||
# @param [Fixnum] index
|
||||
# @return [Oga::XML::Node]
|
||||
#
|
||||
def [](index)
|
||||
@nodes[index]
|
||||
end
|
||||
|
||||
##
|
||||
# Converts the current set to an Array.
|
||||
#
|
||||
# @return [Array]
|
||||
#
|
||||
def to_a
|
||||
@nodes
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new set based on the current and the specified set. The newly
|
||||
# created set does not inherit ownership rules of the current set.
|
||||
#
|
||||
# @param [Oga::XML::NodeSet] other
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def +(other)
|
||||
self.class.new(to_a | other.to_a)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns `true` if the current node set and the one given in `other` are
|
||||
# equal to each other.
|
||||
#
|
||||
# @param [Oga::XML::NodeSet] other
|
||||
#
|
||||
def ==(other)
|
||||
other.is_a?(NodeSet) && other.equal_nodes?(@nodes)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns `true` if the nodes given in `nodes` are equal to those
|
||||
# specified in the current `@nodes` variable. This method allows two
|
||||
# NodeSet instances to compare each other without the need of exposing
|
||||
# `@nodes` to the public.
|
||||
#
|
||||
# @param [Array<Oga::XML::Node>] nodes
|
||||
#
|
||||
def equal_nodes?(nodes)
|
||||
@nodes == nodes
|
||||
end
|
||||
|
||||
##
|
||||
# Adds the nodes of the given node set to the current node set.
|
||||
#
|
||||
# @param [Oga::XML::NodeSet] other
|
||||
#
|
||||
def concat(other)
|
||||
other.each { |node| push(node) }
|
||||
end
|
||||
|
||||
##
|
||||
# Removes the current nodes from their owning set. The nodes are *not*
|
||||
# removed from the current set.
|
||||
#
|
||||
# This method is intended to remove nodes from an XML document/node.
|
||||
#
|
||||
def remove
|
||||
sets = []
|
||||
|
||||
|
@ -270,9 +232,7 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Removes a node from the current set only.
|
||||
#
|
||||
def delete(node)
|
||||
removed = @nodes.delete(node)
|
||||
|
||||
|
@ -285,12 +245,10 @@ module Oga
|
|||
removed
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the values of the given attribute.
|
||||
#
|
||||
# @param [String|Symbol] name The name of the attribute.
|
||||
# @return [Array]
|
||||
#
|
||||
def attribute(name)
|
||||
values = []
|
||||
|
||||
|
@ -305,11 +263,9 @@ module Oga
|
|||
|
||||
alias_method :attr, :attribute
|
||||
|
||||
##
|
||||
# Returns the text of all nodes in the set, ignoring comment nodes.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def text
|
||||
text = ''
|
||||
|
||||
|
@ -322,9 +278,7 @@ module Oga
|
|||
text
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
values = @nodes.map(&:inspect).join(', ')
|
||||
|
||||
|
@ -333,21 +287,17 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Takes ownership of the given node. This only occurs when the current
|
||||
# set has an owner.
|
||||
#
|
||||
# @param [Oga::XML::Node] node
|
||||
#
|
||||
def take_ownership(node)
|
||||
node.node_set = self
|
||||
end
|
||||
|
||||
##
|
||||
# Removes ownership of the node if it belongs to the current set.
|
||||
#
|
||||
# @param [Oga::XML::Node] node
|
||||
#
|
||||
def remove_ownership(node)
|
||||
node.node_set = nil if node.node_set == self
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
%header
|
||||
{
|
||||
##
|
||||
# DOM parser for both XML and HTML.
|
||||
#
|
||||
# This parser does not produce a dedicated AST, instead it emits XML nodes
|
||||
|
@ -205,11 +204,9 @@ string_body
|
|||
|
||||
%inner
|
||||
{
|
||||
##
|
||||
# Hash mapping token types and dedicated error labels.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
TOKEN_ERROR_MAPPING = {
|
||||
:T_STRING => 'string',
|
||||
:T_TEXT => 'text',
|
||||
|
@ -234,11 +231,9 @@ string_body
|
|||
-1 => 'end of input'
|
||||
}
|
||||
|
||||
##
|
||||
# @param [String|IO] data The input to parse.
|
||||
# @param [Hash] options
|
||||
# @see [Oga::XML::Lexer#initialize]
|
||||
#
|
||||
def initialize(data, options = {})
|
||||
@data = data
|
||||
@lexer = Lexer.new(data, options)
|
||||
|
@ -246,20 +241,16 @@ string_body
|
|||
reset
|
||||
end
|
||||
|
||||
##
|
||||
# Resets the internal state of the parser.
|
||||
#
|
||||
def reset
|
||||
@line = 1
|
||||
|
||||
@lexer.reset
|
||||
end
|
||||
|
||||
##
|
||||
# Yields the next token from the lexer.
|
||||
#
|
||||
# @yieldparam [Array]
|
||||
#
|
||||
def each_token
|
||||
@lexer.advance do |type, value, line|
|
||||
@line = line if line
|
||||
|
@ -270,12 +261,10 @@ string_body
|
|||
yield [-1, -1]
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Fixnum] stack_type
|
||||
# @param [Fixnum] stack_value
|
||||
# @param [Symbol] token_type
|
||||
# @param [String] token_value
|
||||
#
|
||||
def parser_error(stack_type, stack_value, token_type, token_value)
|
||||
case id_to_type(stack_type)
|
||||
when :rule
|
||||
|
@ -294,9 +283,7 @@ string_body
|
|||
raise LL::ParserError, message
|
||||
end
|
||||
|
||||
##
|
||||
# @see [LL::Driver#parse]
|
||||
#
|
||||
def parse
|
||||
retval = super
|
||||
|
||||
|
@ -305,10 +292,8 @@ string_body
|
|||
retval
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Array] children
|
||||
# @return [Oga::XML::Document]
|
||||
#
|
||||
def on_document(children = [])
|
||||
document = Document.new(:type => @lexer.html? ? :html : :xml)
|
||||
|
||||
|
@ -327,42 +312,32 @@ string_body
|
|||
document
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
def on_doctype(options = {})
|
||||
Doctype.new(options)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] text
|
||||
# @return [Oga::XML::Cdata]
|
||||
#
|
||||
def on_cdata(text = nil)
|
||||
Cdata.new(:text => text)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] text
|
||||
# @return [Oga::XML::Comment]
|
||||
#
|
||||
def on_comment(text = nil)
|
||||
Comment.new(:text => text)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
# @param [String] text
|
||||
# @return [Oga::XML::ProcessingInstruction]
|
||||
#
|
||||
def on_proc_ins(name, text = nil)
|
||||
ProcessingInstruction.new(:name => name, :text => text)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Array] attributes
|
||||
# @return [Oga::XML::XmlDeclaration]
|
||||
#
|
||||
def on_xml_decl(attributes = [])
|
||||
options = {}
|
||||
|
||||
|
@ -373,20 +348,16 @@ string_body
|
|||
XmlDeclaration.new(options)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] text
|
||||
# @return [Oga::XML::Text]
|
||||
#
|
||||
def on_text(text)
|
||||
Text.new(:text => text)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] namespace
|
||||
# @param [String] name
|
||||
# @param [Hash] attributes
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def on_element(namespace, name, attributes = {})
|
||||
element = Element.new(
|
||||
:namespace_name => namespace,
|
||||
|
@ -397,31 +368,25 @@ string_body
|
|||
element
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::XML::Element] element
|
||||
# @param [Array] children
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def on_element_children(element, children = [])
|
||||
element.children = children
|
||||
|
||||
element
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Oga::XML::Element] element
|
||||
# @return [Oga::XML::Element]
|
||||
#
|
||||
def after_element(element)
|
||||
element
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] name
|
||||
# @param [String] ns_name
|
||||
# @param [String] value
|
||||
# @return [Oga::XML::Attribute]
|
||||
#
|
||||
def on_attribute(name, ns_name = nil, value = nil)
|
||||
Attribute.new(
|
||||
:namespace_name => ns_name,
|
||||
|
@ -430,9 +395,7 @@ string_body
|
|||
)
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Array] attrs
|
||||
#
|
||||
def on_attributes(attrs)
|
||||
attrs
|
||||
end
|
||||
|
|
|
@ -1,34 +1,26 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class used for storing information about a single processing instruction.
|
||||
#
|
||||
class ProcessingInstruction < CharacterNode
|
||||
# @return [String]
|
||||
attr_accessor :name
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :name The name of the instruction.
|
||||
# @see [Oga::XML::CharacterNode#initialize]
|
||||
#
|
||||
def initialize(options = {})
|
||||
super
|
||||
|
||||
@name = options[:name]
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
"<?#{name}#{text}?>"
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
"ProcessingInstruction(name: #{name.inspect} text: #{text.inspect})"
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The PullParser class can be used to parse an XML document incrementally
|
||||
# instead of parsing it as a whole. This results in lower memory usage and
|
||||
# potentially faster parsing times. The downside is that pull parsers are
|
||||
|
@ -18,7 +17,6 @@ module Oga
|
|||
#
|
||||
# This parses yields proper XML instances such as {Oga::XML::Element}.
|
||||
# Doctypes and XML declarations are ignored by this parser.
|
||||
#
|
||||
class PullParser < Parser
|
||||
# @return [Oga::XML::Node]
|
||||
attr_reader :node
|
||||
|
@ -27,9 +25,7 @@ module Oga
|
|||
# @return [Array]
|
||||
attr_reader :nesting
|
||||
|
||||
##
|
||||
# @return [Array]
|
||||
#
|
||||
DISABLED_CALLBACKS = [
|
||||
:on_document,
|
||||
:on_doctype,
|
||||
|
@ -37,9 +33,7 @@ module Oga
|
|||
:on_element_children
|
||||
]
|
||||
|
||||
##
|
||||
# @return [Array]
|
||||
#
|
||||
BLOCK_CALLBACKS = [
|
||||
:on_cdata,
|
||||
:on_comment,
|
||||
|
@ -47,11 +41,9 @@ module Oga
|
|||
:on_proc_ins
|
||||
]
|
||||
|
||||
##
|
||||
# Returns the shorthands that can be used for various node classes.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
NODE_SHORTHANDS = {
|
||||
:text => XML::Text,
|
||||
:node => XML::Node,
|
||||
|
@ -62,9 +54,7 @@ module Oga
|
|||
:xml_declaration => XML::XmlDeclaration
|
||||
}
|
||||
|
||||
##
|
||||
# @see Oga::XML::Parser#reset
|
||||
#
|
||||
def reset
|
||||
super
|
||||
|
||||
|
@ -73,11 +63,9 @@ module Oga
|
|||
@node = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the input and yields every node to the supplied block.
|
||||
#
|
||||
# @yieldparam [Oga::XML::Node]
|
||||
#
|
||||
def parse(&block)
|
||||
@block = block
|
||||
|
||||
|
@ -86,7 +74,6 @@ module Oga
|
|||
return
|
||||
end
|
||||
|
||||
##
|
||||
# Calls the supplied block if the current node type and optionally the
|
||||
# nesting match. This method allows you to write this:
|
||||
#
|
||||
|
@ -120,7 +107,6 @@ module Oga
|
|||
# returned by {Oga::XML::Node#node_type}.
|
||||
#
|
||||
# @param [Array] nesting The element name nesting to act upon.
|
||||
#
|
||||
def on(type, nesting = [])
|
||||
if node.is_a?(NODE_SHORTHANDS[type])
|
||||
if nesting.empty? or nesting == self.nesting
|
||||
|
@ -149,9 +135,7 @@ module Oga
|
|||
EOF
|
||||
end
|
||||
|
||||
##
|
||||
# @see Oga::XML::Parser#on_element
|
||||
#
|
||||
def on_element(*args)
|
||||
@node = super
|
||||
|
||||
|
@ -162,9 +146,7 @@ module Oga
|
|||
return
|
||||
end
|
||||
|
||||
##
|
||||
# @see Oga::XML::Parser#on_element_children
|
||||
#
|
||||
def after_element(*args)
|
||||
nesting.pop
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The Querying module provides methods that make it easy to run XPath/CSS
|
||||
# queries on XML documents/elements.
|
||||
#
|
||||
module Querying
|
||||
##
|
||||
# Evaluates the given XPath expression.
|
||||
#
|
||||
# Querying a document:
|
||||
|
@ -34,7 +31,6 @@ module Oga
|
|||
# be String values.
|
||||
#
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def xpath(expression, variables = {})
|
||||
ast = XPath::Parser.parse_with_cache(expression)
|
||||
block = XPath::Compiler.compile_with_cache(ast)
|
||||
|
@ -42,7 +38,6 @@ module Oga
|
|||
block.call(self, variables)
|
||||
end
|
||||
|
||||
##
|
||||
# Evaluates the XPath expression and returns the first matched node.
|
||||
#
|
||||
# Querying a document:
|
||||
|
@ -59,14 +54,12 @@ module Oga
|
|||
#
|
||||
# @see [#xpath]
|
||||
# @return [Oga::XML::Node|Oga::XML::Attribute]
|
||||
#
|
||||
def at_xpath(*args)
|
||||
result = xpath(*args)
|
||||
|
||||
result.is_a?(XML::NodeSet) ? result.first : result
|
||||
end
|
||||
|
||||
##
|
||||
# Evaluates the given CSS expression.
|
||||
#
|
||||
# Querying a document:
|
||||
|
@ -81,7 +74,6 @@ module Oga
|
|||
#
|
||||
# @param [String] expression The CSS expression to run.
|
||||
# @return [Oga::XML::NodeSet]
|
||||
#
|
||||
def css(expression)
|
||||
ast = CSS::Parser.parse_with_cache(expression)
|
||||
block = XPath::Compiler.compile_with_cache(ast)
|
||||
|
@ -89,12 +81,10 @@ module Oga
|
|||
block.call(self)
|
||||
end
|
||||
|
||||
##
|
||||
# Evaluates the CSS expression and returns the first matched node.
|
||||
#
|
||||
# @see [#css]
|
||||
# @return [Oga::XML::Node|Oga::XML::Attribute]
|
||||
#
|
||||
def at_css(*args)
|
||||
result = css(*args)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# The SaxParser class provides the basic interface for writing custom SAX
|
||||
# parsers. All callback methods defined in {Oga::XML::Parser} are delegated
|
||||
# to a dedicated handler class.
|
||||
|
@ -66,12 +65,9 @@ module Oga
|
|||
# attribute names (optionally prefixed by their namespace) and their values.
|
||||
# You can overwrite `on_attribute` to control individual attributes and
|
||||
# `on_attributes` to control the final set.
|
||||
#
|
||||
class SaxParser < Parser
|
||||
##
|
||||
# @param [Object] handler The SAX handler to delegate callbacks to.
|
||||
# @see [Oga::XML::Parser#initialize]
|
||||
#
|
||||
def initialize(handler, *args)
|
||||
@handler = handler
|
||||
|
||||
|
@ -89,38 +85,32 @@ module Oga
|
|||
EOF
|
||||
end
|
||||
|
||||
##
|
||||
# Manually overwrite `on_element` so we can ensure that `after_element`
|
||||
# always receives the namespace and name.
|
||||
#
|
||||
# @see [Oga::XML::Parser#on_element]
|
||||
# @return [Array]
|
||||
#
|
||||
def on_element(namespace, name, attrs = [])
|
||||
run_callback(:on_element, namespace, name, attrs)
|
||||
|
||||
[namespace, name]
|
||||
end
|
||||
|
||||
##
|
||||
# Manually overwrite `after_element` so it can take a namespace and name.
|
||||
# This differs a bit from the regular `after_element` which only takes an
|
||||
# {Oga::XML::Element} instance.
|
||||
#
|
||||
# @param [Array] namespace_with_name
|
||||
#
|
||||
def after_element(namespace_with_name)
|
||||
run_callback(:after_element, *namespace_with_name)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
##
|
||||
# Manually overwrite this method since for this one we _do_ want the
|
||||
# return value so it can be passed to `on_element`.
|
||||
#
|
||||
# @see [Oga::XML::Parser#on_attribute]
|
||||
#
|
||||
def on_attribute(name, ns = nil, value = nil)
|
||||
if @handler.respond_to?(:on_attribute)
|
||||
return run_callback(:on_attribute, name, ns, value)
|
||||
|
@ -135,12 +125,10 @@ module Oga
|
|||
{key => value}
|
||||
end
|
||||
|
||||
##
|
||||
# Merges the attributes together into a Hash.
|
||||
#
|
||||
# @param [Array] attrs
|
||||
# @return [Hash]
|
||||
#
|
||||
def on_attributes(attrs)
|
||||
if @handler.respond_to?(:on_attributes)
|
||||
return run_callback(:on_attributes, attrs)
|
||||
|
@ -156,9 +144,7 @@ module Oga
|
|||
merged
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] text
|
||||
#
|
||||
def on_text(text)
|
||||
if @handler.respond_to?(:on_text)
|
||||
unless inside_literal_html?
|
||||
|
@ -173,17 +159,13 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def inside_literal_html?
|
||||
@lexer.html_script? || @lexer.html_style?
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Symbol] method
|
||||
# @param [Array] args
|
||||
#
|
||||
def run_callback(method, *args)
|
||||
@handler.send(method, *args) if @handler.respond_to?(method)
|
||||
end
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class containing information about a single text node. Text nodes don't
|
||||
# have any children, attributes and the likes; just text.
|
||||
#
|
||||
class Text < CharacterNode
|
||||
def initialize(*args)
|
||||
super
|
||||
|
@ -11,20 +9,16 @@ module Oga
|
|||
@decoded = false
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] value
|
||||
#
|
||||
def text=(value)
|
||||
@decoded = false
|
||||
@text = value
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the text as a String. Upon the first call any XML/HTML entities
|
||||
# are decoded.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def text
|
||||
if decode_entities?
|
||||
@text = EntityDecoder.try_decode(@text, html?)
|
||||
|
@ -34,9 +28,7 @@ module Oga
|
|||
@text
|
||||
end
|
||||
|
||||
##
|
||||
# @see [Oga::XML::CharacterNode#to_xml]
|
||||
#
|
||||
def to_xml
|
||||
return super if inside_literal_html?
|
||||
|
||||
|
@ -45,16 +37,12 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def decode_entities?
|
||||
!@decoded && !inside_literal_html?
|
||||
end
|
||||
|
||||
##
|
||||
# @return [TrueClass|FalseClass]
|
||||
#
|
||||
def inside_literal_html?
|
||||
node = parent
|
||||
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Module that provides methods to traverse DOM trees.
|
||||
#
|
||||
module Traversal
|
||||
##
|
||||
# Traverses through the node and yields every child node to the supplied
|
||||
# block.
|
||||
#
|
||||
|
@ -29,7 +26,6 @@ module Oga
|
|||
# end
|
||||
#
|
||||
# @yieldparam [Oga::XML::Node] The current node.
|
||||
#
|
||||
def each_node
|
||||
visit = children.to_a.reverse
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
module Oga
|
||||
module XML
|
||||
##
|
||||
# Class containing information about an XML declaration tag.
|
||||
#
|
||||
class XmlDeclaration
|
||||
# @return [String]
|
||||
attr_accessor :version
|
||||
|
@ -14,24 +12,20 @@ module Oga
|
|||
# @return [String]
|
||||
attr_accessor :standalone
|
||||
|
||||
##
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :version
|
||||
# @option options [String] :encoding
|
||||
# @option options [String] :standalone
|
||||
#
|
||||
def initialize(options = {})
|
||||
@version = options[:version] || '1.0'
|
||||
@encoding = options[:encoding] || 'UTF-8'
|
||||
@standalone = options[:standalone]
|
||||
end
|
||||
|
||||
##
|
||||
# Converts the declaration tag to XML.
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def to_xml
|
||||
pairs = []
|
||||
|
||||
|
@ -44,9 +38,7 @@ module Oga
|
|||
"<?xml #{pairs.join(' ')} ?>"
|
||||
end
|
||||
|
||||
##
|
||||
# @return [String]
|
||||
#
|
||||
def inspect
|
||||
segments = []
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
module Oga
|
||||
module XPath
|
||||
##
|
||||
# Compiling of XPath ASTs into Ruby code.
|
||||
#
|
||||
# The Compiler class can be used to turn an XPath AST into Ruby source code
|
||||
|
@ -9,7 +8,6 @@ module Oga
|
|||
# recompiling the same expression over and over again.
|
||||
#
|
||||
# @private
|
||||
#
|
||||
class Compiler
|
||||
# @return [Oga::LRU]
|
||||
CACHE = LRU.new
|
||||
|
@ -36,11 +34,9 @@ module Oga
|
|||
:on_or => [:to_boolean, :or]
|
||||
}
|
||||
|
||||
##
|
||||
# Compiles and caches an AST.
|
||||
#
|
||||
# @see [#compile]
|
||||
#
|
||||
def self.compile_with_cache(ast)
|
||||
CACHE.get_or_set(ast) { new.compile(ast) }
|
||||
end
|
||||
|
@ -57,12 +53,10 @@ module Oga
|
|||
@predicate_indexes = []
|
||||
end
|
||||
|
||||
##
|
||||
# Compiles an XPath AST into a Ruby Proc.
|
||||
#
|
||||
# @param [AST::Node] ast
|
||||
# @return [Proc]
|
||||
#
|
||||
def compile(ast)
|
||||
document = literal(:node)
|
||||
matched = matched_literal
|
||||
|
@ -97,13 +91,11 @@ module Oga
|
|||
reset
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a single XPath AST node.
|
||||
#
|
||||
# @param [AST::Node] ast
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def process(ast, input, &block)
|
||||
send("on_#{ast.type}", ast, input, &block)
|
||||
end
|
||||
|
@ -119,7 +111,6 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Dispatches the processing of axes to dedicated methods. This works
|
||||
# similar to {#process} except the handler names are "on_axis_X" with "X"
|
||||
# being the axis name.
|
||||
|
@ -127,7 +118,6 @@ module Oga
|
|||
# @param [AST::Node] ast
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_axis(ast, input, &block)
|
||||
name, test, following = *ast
|
||||
|
||||
|
@ -425,14 +415,12 @@ module Oga
|
|||
ast
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a predicate that requires a temporary NodeSet.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @param [AST::Node] test
|
||||
# @param [AST::Node] predicate
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_predicate_temporary(input, test, predicate)
|
||||
temp_set = unique_literal(:temp_set)
|
||||
pred_node = unique_literal(:pred_node)
|
||||
|
@ -472,14 +460,12 @@ module Oga
|
|||
ast
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a predicate that doesn't require temporary NodeSet.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @param [AST::Node] test
|
||||
# @param [AST::Node] predicate
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_predicate_direct(input, test, predicate)
|
||||
pred_var = unique_literal(:pred_var)
|
||||
index_var = predicate_index
|
||||
|
@ -514,14 +500,12 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Processes a predicate that uses a literal index.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @param [AST::Node] test
|
||||
# @param [AST::Node] predicate
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_predicate_index(input, test, predicate)
|
||||
index_var = predicate_index
|
||||
index_step = literal(1)
|
||||
|
@ -549,11 +533,9 @@ module Oga
|
|||
name_match ? condition.and(name_match) : condition
|
||||
end
|
||||
|
||||
##
|
||||
# Processes the `=` operator.
|
||||
#
|
||||
# @see [#operator]
|
||||
#
|
||||
def on_eq(ast, input, &block)
|
||||
conv = literal(Conversion)
|
||||
|
||||
|
@ -567,11 +549,9 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Processes the `!=` operator.
|
||||
#
|
||||
# @see [#operator]
|
||||
#
|
||||
def on_neq(ast, input, &block)
|
||||
conv = literal(Conversion)
|
||||
|
||||
|
@ -599,11 +579,9 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Processes the `|` operator.
|
||||
#
|
||||
# @see [#operator]
|
||||
#
|
||||
def on_pipe(ast, input, &block)
|
||||
left, right = *ast
|
||||
|
||||
|
@ -649,13 +627,11 @@ module Oga
|
|||
.or(send_message(:raise, string("Undefined XPath variable: #{name}")))
|
||||
end
|
||||
|
||||
##
|
||||
# Delegates function calls to specific handlers.
|
||||
#
|
||||
# @param [AST::Node] ast
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_call(ast, input, &block)
|
||||
name, *args = *ast
|
||||
|
||||
|
@ -808,7 +784,6 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Processes the `id()` function call.
|
||||
#
|
||||
# The XPath specification states that this function's behaviour should be
|
||||
|
@ -825,7 +800,6 @@ module Oga
|
|||
# @param [Oga::Ruby::Node] input
|
||||
# @param [AST::Node] arg
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_call_id(input, arg)
|
||||
orig_input = original_input_literal
|
||||
node = unique_literal(:node)
|
||||
|
@ -1270,13 +1244,11 @@ module Oga
|
|||
index.to_f
|
||||
end
|
||||
|
||||
##
|
||||
# Delegates type tests to specific handlers.
|
||||
#
|
||||
# @param [AST::Node] ast
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def on_type_test(ast, input, &block)
|
||||
name, following = *ast
|
||||
|
||||
|
@ -1414,13 +1386,11 @@ module Oga
|
|||
condition
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an AST matching the first node of a node set.
|
||||
#
|
||||
# @param [Oga::Ruby::Node] ast
|
||||
# @param [Oga::Ruby::Node] input
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def match_first_node(ast, input)
|
||||
catch_message(:value) do
|
||||
process(ast, input) do |node|
|
||||
|
@ -1429,11 +1399,9 @@ module Oga
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Tries to match the first node in a set, otherwise processes it as usual.
|
||||
#
|
||||
# @see [#match_first_node]
|
||||
#
|
||||
def try_match_first_node(ast, input, optimize_first = true)
|
||||
if return_nodeset?(ast) and optimize_first
|
||||
match_first_node(ast, input)
|
||||
|
@ -1460,7 +1428,6 @@ module Oga
|
|||
arg_var.assign(arg_ast).followed_by { yield arg_var }
|
||||
end
|
||||
|
||||
##
|
||||
# Generates the code for an operator.
|
||||
#
|
||||
# The generated code is optimized so that expressions such as `a/b = c`
|
||||
|
@ -1479,7 +1446,6 @@ module Oga
|
|||
# @param [Oga::Ruby::Node] input
|
||||
# @param [TrueClass|FalseClass] optimize_first
|
||||
# @return [Oga::Ruby::Node]
|
||||
#
|
||||
def operator(ast, input, optimize_first = true)
|
||||
left, right = *ast
|
||||
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
module Oga
|
||||
module XPath
|
||||
##
|
||||
# Module for converting XPath objects such as NodeSets.
|
||||
#
|
||||
# @private
|
||||
#
|
||||
module Conversion
|
||||
##
|
||||
# Converts both arguments to a type that can be compared using ==.
|
||||
#
|
||||
# @return [Array]
|
||||
#
|
||||
def self.to_compatible_types(left, right)
|
||||
if left.is_a?(XML::NodeSet) or left.respond_to?(:text)
|
||||
left = to_string(left)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
module Oga
|
||||
module XPath
|
||||
##
|
||||
# Lexer for turning XPath expressions into a set of tokens. Tokens are
|
||||
# returned as arrays with every array having two values:
|
||||
#
|
||||
|
@ -33,18 +32,15 @@ module Oga
|
|||
# shared state.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
class Lexer
|
||||
%% write data;
|
||||
|
||||
# % fix highlight
|
||||
|
||||
##
|
||||
# Maps certain XPath axes written in their short form to their long form
|
||||
# equivalents.
|
||||
#
|
||||
# @return [Hash]
|
||||
#
|
||||
AXIS_MAPPING = {
|
||||
'@' => 'attribute',
|
||||
'//' => 'descendant-or-self',
|
||||
|
@ -52,33 +48,25 @@ module Oga
|
|||
'.' => 'self'
|
||||
}
|
||||
|
||||
##
|
||||
# Axes that require a separate `node()` call to be emitted.
|
||||
#
|
||||
# @return [Array]
|
||||
#
|
||||
AXIS_EMIT_NODE = %w{descendant-or-self parent self}
|
||||
|
||||
##
|
||||
# Axes that require an extra T_SLASH token to be emitted.
|
||||
#
|
||||
# @return [Array]
|
||||
#
|
||||
AXIS_EMIT_EXTRA_SLASH = %w{descendant-or-self}
|
||||
|
||||
##
|
||||
# @param [String] data The data to lex.
|
||||
#
|
||||
def initialize(data)
|
||||
@data = data
|
||||
end
|
||||
|
||||
##
|
||||
# Gathers all the tokens for the input and returns them as an Array.
|
||||
#
|
||||
# @see [#advance]
|
||||
# @return [Array]
|
||||
#
|
||||
def lex
|
||||
tokens = []
|
||||
|
||||
|
@ -89,7 +77,6 @@ module Oga
|
|||
return tokens
|
||||
end
|
||||
|
||||
##
|
||||
# Advances through the input and generates the corresponding tokens. Each
|
||||
# token is yielded to the supplied block.
|
||||
#
|
||||
|
@ -103,7 +90,6 @@ module Oga
|
|||
# the lexer loop has finished.
|
||||
#
|
||||
# @see [#add_token]
|
||||
#
|
||||
def advance(&block)
|
||||
@block = block
|
||||
|
||||
|
@ -137,7 +123,6 @@ module Oga
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Emits a token of which the value is based on the supplied start/stop
|
||||
# position.
|
||||
#
|
||||
|
@ -147,25 +132,21 @@ module Oga
|
|||
#
|
||||
# @see [#text]
|
||||
# @see [#add_token]
|
||||
#
|
||||
def emit(type, start, stop)
|
||||
value = slice_input(start, stop)
|
||||
|
||||
add_token(type, value)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the text between the specified start and stop position.
|
||||
#
|
||||
# @param [Fixnum] start
|
||||
# @param [Fixnum] stop
|
||||
# @return [String]
|
||||
#
|
||||
def slice_input(start, stop)
|
||||
return @data.byteslice(start, stop - start)
|
||||
end
|
||||
|
||||
##
|
||||
# Yields a new token to the supplied block.
|
||||
#
|
||||
# @param [Symbol] type The token type.
|
||||
|
@ -173,7 +154,6 @@ module Oga
|
|||
#
|
||||
# @yieldparam [Symbol] type
|
||||
# @yieldparam [String|NilClass] value
|
||||
#
|
||||
def add_token(type, value = nil)
|
||||
@block.call(type, value)
|
||||
end
|
||||
|
@ -228,7 +208,6 @@ module Oga
|
|||
#
|
||||
# Strings can be single or double quoted. They are mainly used for
|
||||
# attribute values.
|
||||
#
|
||||
dquote = '"';
|
||||
squote = "'";
|
||||
|
||||
|
@ -244,7 +223,6 @@ module Oga
|
|||
# Full Axes
|
||||
#
|
||||
# XPath axes in their full syntax.
|
||||
#
|
||||
axis_full = ('ancestor'
|
||||
| 'ancestor-or-self'
|
||||
| 'attribute'
|
||||
|
@ -268,7 +246,6 @@ module Oga
|
|||
# XPath axes in their abbreviated form. When lexing these are mapped to
|
||||
# their full forms so that the parser doesn't have to take care of
|
||||
# this.
|
||||
#
|
||||
axis_short = '@' | '//' | '..' | '.';
|
||||
|
||||
action emit_axis_short {
|
||||
|
@ -358,7 +335,6 @@ module Oga
|
|||
# can not assign variables in an expression, you can only refer to them.
|
||||
# This means that libraries themselves have to expose an interface for
|
||||
# setting variables.
|
||||
#
|
||||
var = '$' identifier;
|
||||
|
||||
action emit_variable {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
%header
|
||||
{
|
||||
##
|
||||
# AST parser for XPath expressions. The AST is built using `AST::Node`
|
||||
# instances.
|
||||
#
|
||||
|
@ -248,42 +247,32 @@ variable
|
|||
|
||||
%inner
|
||||
{
|
||||
##
|
||||
# @return [Oga::LRU]
|
||||
#
|
||||
CACHE = LRU.new
|
||||
|
||||
##
|
||||
# @param [String] data
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def self.parse_with_cache(data)
|
||||
CACHE.get_or_set(data) { new(data).parse }
|
||||
end
|
||||
|
||||
##
|
||||
# @param [String] data The input to parse.
|
||||
#
|
||||
def initialize(data)
|
||||
@lexer = Lexer.new(data)
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new XPath node.
|
||||
#
|
||||
# @param [Symbol] type
|
||||
# @param [Array] children
|
||||
# @return [AST::Node]
|
||||
#
|
||||
def s(type, *children)
|
||||
AST::Node.new(type, children)
|
||||
end
|
||||
|
||||
##
|
||||
# Yields the next token from the lexer.
|
||||
#
|
||||
# @yieldparam [Array]
|
||||
#
|
||||
def each_token
|
||||
@lexer.advance do |type, value, line|
|
||||
@line = line if line
|
||||
|
@ -294,9 +283,7 @@ variable
|
|||
yield [-1, -1]
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Array] val
|
||||
#
|
||||
def combine_operators(val)
|
||||
ret = val[0]
|
||||
|
||||
|
@ -307,9 +294,7 @@ variable
|
|||
ret
|
||||
end
|
||||
|
||||
##
|
||||
# @param [Array] val
|
||||
#
|
||||
def combine_optional_operator(val)
|
||||
ret = val[0]
|
||||
|
||||
|
|
Loading…
Reference in New Issue