arboretum 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/arboretum.rb +2 -2
- data/lib/arboretum/debtor.rb +49 -0
- data/lib/arboretum/doctree.rb +105 -47
- data/lib/arboretum/scandent.rb +92 -104
- data/lib/arboretum/xml.rb +6 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53ab23c0b718279ac0f58181b1b57cc75a610aa1
|
4
|
+
data.tar.gz: 964672ec600783cb39cad917b61deba000bd544f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f141ef672014ac90376357b7edad9335e6653d27eebd23620bdce06e4f5bc58ce8da6890c1cf6f0255de622f52b08129b88dff9932e6ff5e36b6dfe48a9c837
|
7
|
+
data.tar.gz: d1f8c0b8e694062e19ca877c1fcb3a4c1aae57eb8856615660f7f6a33d26cde5d0e37d8356f948e2ced718668ded81c52c8fead8bdb3c3ed49a3fd317cc932ec
|
data/lib/arboretum.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
require_relative 'arboretum/
|
1
|
+
require_relative 'arboretum/scandent'
|
2
2
|
require_relative 'arboretum/doctree'
|
3
|
-
require_relative 'arboretum/
|
3
|
+
require_relative 'arboretum/xml'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'fiber'
|
2
|
+
|
3
|
+
class ArboretumFiber
|
4
|
+
def self.ROOT_FIBER
|
5
|
+
@@ROOT_FIBER
|
6
|
+
end
|
7
|
+
def self.set_root_fiber(fiber)
|
8
|
+
@@ROOT_FIBER = fiber
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
ArboretumFiber.set_root_fiber(Fiber.current)
|
13
|
+
|
14
|
+
class Debtor
|
15
|
+
def initialize
|
16
|
+
@resource = nil
|
17
|
+
@locked = true
|
18
|
+
@creditors = Array.new
|
19
|
+
end
|
20
|
+
def set_resource(rsc)
|
21
|
+
@resource = rsc
|
22
|
+
end
|
23
|
+
alias_method :set, :set_resource
|
24
|
+
def resource
|
25
|
+
begin
|
26
|
+
if locked?
|
27
|
+
@creditors << Fiber.current
|
28
|
+
Fiber.yield
|
29
|
+
end
|
30
|
+
rescue FiberError
|
31
|
+
puts "Warning: Tried to yield the root Fiber on a locked resource call. Returning resource anyways..."
|
32
|
+
end
|
33
|
+
@resource
|
34
|
+
end
|
35
|
+
def resource!
|
36
|
+
@resource
|
37
|
+
end
|
38
|
+
def locked?
|
39
|
+
@locked
|
40
|
+
end
|
41
|
+
def free?
|
42
|
+
!@locked
|
43
|
+
end
|
44
|
+
def free
|
45
|
+
@locked = false
|
46
|
+
@creditors.each {|c| c.resume }
|
47
|
+
@creditors = Array.new
|
48
|
+
end
|
49
|
+
end
|
data/lib/arboretum/doctree.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'promise'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'securerandom'
|
4
|
+
|
1
5
|
module Arboretum
|
2
6
|
module DocTree
|
3
7
|
module Counters
|
@@ -60,21 +64,21 @@ module Arboretum
|
|
60
64
|
end # Counters
|
61
65
|
|
62
66
|
module Elements
|
63
|
-
require 'forwardable'
|
64
|
-
require 'securerandom'
|
65
|
-
require_relative 'scandent'
|
66
|
-
|
67
67
|
# Tree is a representation of a tree data structure consisting of elements
|
68
68
|
# A Tree holds only reference to the root Element of the tree
|
69
69
|
# A tree is useful for contextual operations on elements with the root as an ancestor
|
70
70
|
class Tree
|
71
71
|
include Enumerable
|
72
72
|
|
73
|
-
attr_accessor :root
|
73
|
+
attr_accessor :root, :id_cache
|
74
74
|
|
75
75
|
def initialize(root=nil)
|
76
|
-
@root = root
|
77
|
-
@listeners =
|
76
|
+
@root = root # Element
|
77
|
+
@listeners = Array.new # Array of GroupListeners
|
78
|
+
|
79
|
+
@id_cache = Hash.new # Hash: String => Element
|
80
|
+
|
81
|
+
root.update_tree_residence(self)
|
78
82
|
end
|
79
83
|
|
80
84
|
# Redefine the `each` method to iterate through all elements in the tree in depth-first order
|
@@ -94,6 +98,24 @@ module Arboretum
|
|
94
98
|
Array.new.tap {|list| self.each {|element| list << element}}
|
95
99
|
end
|
96
100
|
|
101
|
+
def id_cache_add(id, element)
|
102
|
+
@id_cache[id] = element
|
103
|
+
end
|
104
|
+
|
105
|
+
def id_cache_remove(id)
|
106
|
+
@id_cache.delete(id)
|
107
|
+
end
|
108
|
+
|
109
|
+
def id_cache_get(id)
|
110
|
+
@id_cache[id].tap {|element| yield element if block_given?}
|
111
|
+
end
|
112
|
+
alias_method :element_from_id, :id_cache_get
|
113
|
+
|
114
|
+
def element_from_id_hash(hash_id)
|
115
|
+
raise IndexError.new("Tried to get element from hash id with no hash: #{hash_id}") if !hash_id[0].eql?("#")
|
116
|
+
self.id_cache_get(hash_id[1..-1]).tap {|element| yield element if block_given?}
|
117
|
+
end
|
118
|
+
|
97
119
|
# Find any and all elements in the tree that match a given ScandentRule string
|
98
120
|
def scan(rule_string)
|
99
121
|
selected = []
|
@@ -387,6 +409,7 @@ module Arboretum
|
|
387
409
|
element = Element.create(element) if element.is_a?(Hash)
|
388
410
|
element.graft_onto(self.parent, self.index_in_parent)
|
389
411
|
self.graft_onto(element, index)
|
412
|
+
yield element if block_given?
|
390
413
|
element
|
391
414
|
end
|
392
415
|
end
|
@@ -424,7 +447,7 @@ module Arboretum
|
|
424
447
|
attr_writer :parent, :children, :sibling_prev, :sibling_next, :incrementers, :resetters, :library
|
425
448
|
|
426
449
|
public
|
427
|
-
attr_reader :parent, :children, :sibling_prev, :sibling_next, :incrementers, :resetters, :library
|
450
|
+
attr_reader :parent, :children, :sibling_prev, :sibling_next, :incrementers, :resetters, :library, :tree_residence
|
428
451
|
attr_accessor :break_within, :history
|
429
452
|
|
430
453
|
# Class method to stitch together two elements as siblings, ordered
|
@@ -461,7 +484,7 @@ module Arboretum
|
|
461
484
|
end
|
462
485
|
created_element = TaggedElement.new(namespace, tag, sanitary_attrs)
|
463
486
|
if !text.nil?
|
464
|
-
raise TypeError.new("Text must be a
|
487
|
+
raise TypeError.new("Text must be a String or TextElement") if !(text.is_a?(String) or text.is_a?(TextElement))
|
465
488
|
if text.is_a?(String)
|
466
489
|
text_child = TextElement.new(text)
|
467
490
|
created_element.append_child(text_child)
|
@@ -484,6 +507,9 @@ module Arboretum
|
|
484
507
|
@sibling_next = nil # Element
|
485
508
|
@children = [] # Array of Elements
|
486
509
|
|
510
|
+
# Tree residence
|
511
|
+
@tree_residence = nil # Tree
|
512
|
+
|
487
513
|
# Properties, references, and counters
|
488
514
|
@break_within = false # Boolean
|
489
515
|
@incrementers = Hash.new # Hash with key: Symbol (name), value: Incrementer
|
@@ -535,7 +561,7 @@ module Arboretum
|
|
535
561
|
alias_method :unshift, :prepend_child
|
536
562
|
|
537
563
|
# Insert an element as a child at a specified index it this element's children
|
538
|
-
def insert_child(*elements)
|
564
|
+
def insert_child(*elements, index)
|
539
565
|
placed = Array.new
|
540
566
|
elements.each do |element|
|
541
567
|
if !element.nil?
|
@@ -659,19 +685,15 @@ module Arboretum
|
|
659
685
|
# Add an element to a category in this element's library
|
660
686
|
def lib_add(other, category)
|
661
687
|
raise TypeError.new("Cannot create a record for a non-element") if !other.is_a?(Element) and !other.is_a?(ElementGroup)
|
688
|
+
category = category.to_sym if !category.is_a?(Symbol)
|
662
689
|
if @library.has_key?(category)
|
663
|
-
|
664
|
-
if record.is_a?(Element) and other.is_a?(Element)
|
665
|
-
@library[category] = ElementGroup.new([record, other])
|
666
|
-
elsif record.is_a?(ElementGroup) and other.is_a?(Element)
|
690
|
+
if other.is_a?(Element)
|
667
691
|
@library[category] << other
|
668
|
-
|
669
|
-
@library[category] =
|
670
|
-
elsif record.is_a?(ElementGroup) and other.is_a?(ElementGroup)
|
671
|
-
@library[category] = record + other
|
692
|
+
else
|
693
|
+
@library[category] = @library[category] + other
|
672
694
|
end
|
673
695
|
else
|
674
|
-
@library[category] = other
|
696
|
+
@library[category] = ElementGroup.new([other])
|
675
697
|
end
|
676
698
|
|
677
699
|
end
|
@@ -680,6 +702,7 @@ module Arboretum
|
|
680
702
|
|
681
703
|
# Get record from the given category in this element's library
|
682
704
|
def lib_get(category)
|
705
|
+
category = category.to_sym if !category.is_a?(Symbol)
|
683
706
|
@library[category]
|
684
707
|
end
|
685
708
|
alias_method :[], :lib_get
|
@@ -697,6 +720,11 @@ module Arboretum
|
|
697
720
|
element_copy
|
698
721
|
end
|
699
722
|
|
723
|
+
def update_tree_residence(update_tree)
|
724
|
+
@tree_residence = update_tree
|
725
|
+
@children.each {|child| child.update_tree_residence(update_tree)}
|
726
|
+
end
|
727
|
+
|
700
728
|
# Detach from current parent/siblings
|
701
729
|
def detach
|
702
730
|
Element.stitch!(@sibling_prev, @sibling_next)
|
@@ -705,6 +733,8 @@ module Arboretum
|
|
705
733
|
end
|
706
734
|
alias_method :prune, :detach
|
707
735
|
alias_method :delete, :detach
|
736
|
+
alias_method :clear, :detach
|
737
|
+
alias_method :remove, :detach
|
708
738
|
|
709
739
|
# Graft onto another element of the tree at any index of its children
|
710
740
|
# By default, it will graft as the last element of the other element's children
|
@@ -730,6 +760,8 @@ module Arboretum
|
|
730
760
|
next_child = graft_parent.children[index]
|
731
761
|
Element.stitch!(self, next_child)
|
732
762
|
|
763
|
+
self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)
|
764
|
+
|
733
765
|
# Graft group at index
|
734
766
|
graft_parent.children.insert(index, self)
|
735
767
|
end
|
@@ -747,6 +779,8 @@ module Arboretum
|
|
747
779
|
Element.stitch!(nil, self)
|
748
780
|
Element.stitch!(self, next_child)
|
749
781
|
|
782
|
+
self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)
|
783
|
+
|
750
784
|
# Insert graft group at the beginning of parent children
|
751
785
|
graft_parent.children.insert(0, self)
|
752
786
|
end
|
@@ -763,6 +797,8 @@ module Arboretum
|
|
763
797
|
Element.stitch!(previous_child, self)
|
764
798
|
Element.stitch!(self, nil)
|
765
799
|
|
800
|
+
self.update_tree_residence(graft_parent.tree_residence) if !self.tree_residence.eql?(graft_parent.tree_residence)
|
801
|
+
|
766
802
|
# Push graft group onto parent children
|
767
803
|
graft_parent.children.push(self)
|
768
804
|
end
|
@@ -850,18 +886,18 @@ module Arboretum
|
|
850
886
|
end
|
851
887
|
|
852
888
|
# Finds elements in relation to this one that fit a ScandentRule string
|
853
|
-
def find(rule_string)
|
889
|
+
def find(rule_string, silent: false)
|
854
890
|
rule = Arboretum::Scandent::Parser.parse_rule_string(rule_string, :PATH_LOCATOR)
|
855
891
|
selected = rule.locate(self) {|found_element| yield found_element if block_given?}
|
856
|
-
puts "--Warning: Rule #{rule} did not match any elements!--" if selected.empty?
|
892
|
+
puts "--Warning: Rule #{rule} did not match any elements!--" if selected.empty? and !silent
|
857
893
|
ElementGroup.new(selected)
|
858
894
|
end
|
859
895
|
alias_method :locate, :find
|
860
896
|
|
861
897
|
# Finds up to `n` elements in relation to this one that fit a ScandentRule string
|
862
|
-
def find_first_n(rule_string, limit)
|
898
|
+
def find_first_n(rule_string, limit, silent: false)
|
863
899
|
if limit.zero?
|
864
|
-
puts "--Warning: Rule #{rule} was given limit '0'. Returning nil...--" if selected.empty?
|
900
|
+
puts "--Warning: Rule #{rule} was given limit '0'. Returning nil...--" if selected.empty? and !silent
|
865
901
|
return nil
|
866
902
|
end
|
867
903
|
selected = []
|
@@ -871,14 +907,14 @@ module Arboretum
|
|
871
907
|
yield found_element if block_given?
|
872
908
|
selected << found_element
|
873
909
|
end
|
874
|
-
puts "--Warning: Rule #{rule} did not match any elements!--" if selected.empty?
|
910
|
+
puts "--Warning: Rule #{rule} on #{self.to_s} did not match any elements!--" if selected.empty? and !silent
|
875
911
|
ElementGroup.new(selected)
|
876
912
|
end
|
877
913
|
alias_method :locate_first_n, :find_first_n
|
878
914
|
|
879
915
|
# Find the first element in relation to this one that fits a ScandentRule string
|
880
|
-
def find_first(rule_string)
|
881
|
-
self.find_first_n(rule_string, 1) {|found_element| yield found_element if block_given?}.first
|
916
|
+
def find_first(rule_string, silent: false)
|
917
|
+
self.find_first_n(rule_string, 1, :silent => silent) {|found_element| yield found_element if block_given?}.first
|
882
918
|
end
|
883
919
|
alias_method :locate_first, :find_first
|
884
920
|
|
@@ -970,7 +1006,6 @@ module Arboretum
|
|
970
1006
|
class TaggedElement < Element
|
971
1007
|
@@unpaired_tags = [:'DOCTYPE', :'!DOCTYPE', :'area', :'base', :'br', :'col', :'command', :'embed', :'hr', :'img',
|
972
1008
|
:'input', :'keygen', :'link', :'meta', :'param', :'source', :'track', :'wbr']
|
973
|
-
@@need_valid_xml = true
|
974
1009
|
|
975
1010
|
attr_accessor :namespace, :tag, :attrs
|
976
1011
|
|
@@ -980,7 +1015,7 @@ module Arboretum
|
|
980
1015
|
# Element tag and attributes
|
981
1016
|
@namespace = namespace # Symbol
|
982
1017
|
@tag = tag # Symbol
|
983
|
-
@attrs = attrs # Hash
|
1018
|
+
@attrs = attrs # Hash: Symbol => Array of Strings
|
984
1019
|
end
|
985
1020
|
|
986
1021
|
def self.paired?(tag)
|
@@ -994,6 +1029,15 @@ module Arboretum
|
|
994
1029
|
element_copy
|
995
1030
|
end
|
996
1031
|
|
1032
|
+
def update_tree_residence(update_tree)
|
1033
|
+
# Update old and new tree id cache
|
1034
|
+
if self.has_attr?(:id)
|
1035
|
+
self.tree_residence&.id_cache_remove(self.attr_value_str(:id))
|
1036
|
+
update_tree&.id_cache_add(self.attr_value_str(:id), self)
|
1037
|
+
end
|
1038
|
+
super(update_tree)
|
1039
|
+
end
|
1040
|
+
|
997
1041
|
# Returns the id of this element, or an auto-assigned one if none exists
|
998
1042
|
def ref
|
999
1043
|
if !self.has_attr?(:id) or self.attr_value_str(:id).nil?
|
@@ -1005,6 +1049,7 @@ module Arboretum
|
|
1005
1049
|
end
|
1006
1050
|
end
|
1007
1051
|
|
1052
|
+
# Returns the '#'-prefixed id of this element, or an auto-assigned one if none exists
|
1008
1053
|
def href
|
1009
1054
|
"#" << self.ref
|
1010
1055
|
end
|
@@ -1018,6 +1063,10 @@ module Arboretum
|
|
1018
1063
|
|
1019
1064
|
def del_attr(attr_name)
|
1020
1065
|
attr_name = attr_name.to_sym if !attr_name.is_a?(Symbol)
|
1066
|
+
|
1067
|
+
# Update tree id cache
|
1068
|
+
self.tree_residence&.id_cache_remove(self.attr_value_str(:id)) if attr_name.eql?(:id)
|
1069
|
+
|
1021
1070
|
self.attrs.delete(attr_name)
|
1022
1071
|
end
|
1023
1072
|
|
@@ -1029,7 +1078,13 @@ module Arboretum
|
|
1029
1078
|
raise TypeError.new("Attribute value must be a String or Array of Strings") if !attr_value.is_a?(Array)
|
1030
1079
|
attr_value.each{|sub_val| raise TypeError.new("Each attribute value in an array must be a String") if !sub_val.is_a?(String)}
|
1031
1080
|
|
1081
|
+
# Update tree id cache
|
1082
|
+
self.tree_residence&.id_cache_remove(self.attr_value_str(:id)) if attr_name.eql?(:id)
|
1083
|
+
|
1032
1084
|
self.attrs[attr_name] = attr_value
|
1085
|
+
|
1086
|
+
# Update tree id cache
|
1087
|
+
self.tree_residence&.id_cache_add(self.attr_value_str(:id), self) if attr_name.eql?(:id)
|
1033
1088
|
end
|
1034
1089
|
|
1035
1090
|
def del_attr_value(attr_name, attr_value)
|
@@ -1040,7 +1095,13 @@ module Arboretum
|
|
1040
1095
|
raise TypeError.new("Attribute value must be a String or Array of Strings") if !attr_value.is_a?(Array)
|
1041
1096
|
attr_value.each{|sub_val| raise TypeError.new("Each attribute value in an array must be a String") if !sub_val.is_a?(String)}
|
1042
1097
|
|
1098
|
+
# Update tree id cache
|
1099
|
+
self.tree_residence&.id_cache_remove(self.attr_value_str(:id)) if attr_name.eql?(:id)
|
1100
|
+
|
1043
1101
|
self.attrs[attr_name].delete_if {|sub_val| attr_value.include?(sub_val)}
|
1102
|
+
|
1103
|
+
# Update tree id cache
|
1104
|
+
self.tree_residence&.id_cache_add(self.attr_value_str(:id), self) if (attr_name.eql?(:id) and !self.attr_value_str.nil?)
|
1044
1105
|
end
|
1045
1106
|
|
1046
1107
|
def add_attr_value(attr_name, attr_value)
|
@@ -1055,6 +1116,8 @@ module Arboretum
|
|
1055
1116
|
else
|
1056
1117
|
self.attrs[attr_name] = attr_value
|
1057
1118
|
end
|
1119
|
+
# Update tree id cache
|
1120
|
+
self.tree_residence&.id_cache_add(self.attr_value_str(:id), self) if attr_name.eql?(:id)
|
1058
1121
|
end
|
1059
1122
|
|
1060
1123
|
def set_tag(new_tag)
|
@@ -1112,9 +1175,7 @@ module Arboretum
|
|
1112
1175
|
values.each do |v|
|
1113
1176
|
element_string << "#{v.gsub('&','&').gsub('<', '<').gsub('>','>')} "
|
1114
1177
|
end
|
1115
|
-
|
1116
|
-
element_string = element_string[0..-2]
|
1117
|
-
end
|
1178
|
+
element_string = element_string.chop unless values.empty?
|
1118
1179
|
element_string << "\""
|
1119
1180
|
end
|
1120
1181
|
# Close the tag if document must have valid xml
|
@@ -1135,21 +1196,11 @@ module Arboretum
|
|
1135
1196
|
self.attrs.each do |key, values|
|
1136
1197
|
element_string << " #{key}"
|
1137
1198
|
element_string << "=\""
|
1138
|
-
values.each
|
1139
|
-
|
1140
|
-
end
|
1141
|
-
if not values.empty?
|
1142
|
-
element_string = element_string[0..-2]
|
1143
|
-
end
|
1199
|
+
values.each {|v| element_string << "#{v} "}
|
1200
|
+
element_string = element_string.chop unless values.empty?
|
1144
1201
|
element_string << "\""
|
1145
1202
|
end
|
1146
|
-
|
1147
|
-
if self.paired? or not @@need_valid_xml
|
1148
|
-
element_string << ">"
|
1149
|
-
else
|
1150
|
-
element_string << " />"
|
1151
|
-
end
|
1152
|
-
element_string
|
1203
|
+
element_string << (self.paired? ? ">" : "/>")
|
1153
1204
|
end
|
1154
1205
|
|
1155
1206
|
def to_s_close
|
@@ -1283,10 +1334,17 @@ module Arboretum
|
|
1283
1334
|
@group
|
1284
1335
|
end
|
1285
1336
|
|
1337
|
+
def only
|
1338
|
+
raise IndexError.new("Element is not the only in its group") if not @group.length == 1
|
1339
|
+
@group.first
|
1340
|
+
end
|
1341
|
+
|
1286
1342
|
def text_string
|
1287
|
-
full_string
|
1288
|
-
|
1289
|
-
|
1343
|
+
String.new.tap{|full_string| @group.each {|element| full_string << element.text_string} }
|
1344
|
+
end
|
1345
|
+
|
1346
|
+
def text_elements
|
1347
|
+
Array.new.tap{|full_list| @group.each {|element| full_list << element.text_elements} }
|
1290
1348
|
end
|
1291
1349
|
|
1292
1350
|
def +(other)
|
data/lib/arboretum/scandent.rb
CHANGED
@@ -36,7 +36,7 @@ module Arboretum
|
|
36
36
|
def to_s
|
37
37
|
rule_str = ''
|
38
38
|
@paths.each do |path|
|
39
|
-
rule_str << ', '
|
39
|
+
rule_str << ', ' unless rule_str.empty?
|
40
40
|
rule_str << path.to_s
|
41
41
|
end
|
42
42
|
rule_str
|
@@ -226,8 +226,8 @@ module Arboretum
|
|
226
226
|
!@attrs[:matches].empty? or
|
227
227
|
(element_ref == :ELEMENT_ROOT and !element.parent.nil?)
|
228
228
|
end
|
229
|
-
@pseudo_exps.each do |
|
230
|
-
return false if !
|
229
|
+
@pseudo_exps.each do |pseudo_name, pseudo_arg|
|
230
|
+
return false if !PseudoClasses.match(element, pseudo_name, pseudo_arg)
|
231
231
|
end
|
232
232
|
@valid_rules.each do |rule|
|
233
233
|
return false if !rule.valid_on?(element)
|
@@ -361,9 +361,18 @@ module Arboretum
|
|
361
361
|
end
|
362
362
|
end
|
363
363
|
|
364
|
-
class
|
364
|
+
class PseudoClasses
|
365
|
+
# Call a pseudo class function
|
365
366
|
def self.match(element, pseudo_name, pseudo_arg)
|
366
|
-
|
367
|
+
PseudoClasses.public_send(pseudo_name.to_sym, element, pseudo_arg)
|
368
|
+
end
|
369
|
+
|
370
|
+
# Pseudo class functions start here
|
371
|
+
def self.header(element, pseudo_arg)
|
372
|
+
(element.is_a?(Arboretum::DocTree::Elements::TaggedElement) and !(/h\d+/.match(element.tag.to_s).nil?))
|
373
|
+
end
|
374
|
+
def self.not(element, pseudo_arg)
|
375
|
+
!element.matches_rule?(pseudo_arg)
|
367
376
|
end
|
368
377
|
end
|
369
378
|
|
@@ -372,14 +381,14 @@ module Arboretum
|
|
372
381
|
# ScandentRules can then be matched to Elements in a DocTree
|
373
382
|
class Parser
|
374
383
|
@@actions = {
|
375
|
-
:T_SLASH
|
376
|
-
:T_SLASH2
|
377
|
-
:T_SLASHDOT2
|
378
|
-
:T_SLASHDOT3
|
379
|
-
:T_SLASHGT
|
380
|
-
:T_SLASHGT2
|
381
|
-
:T_SLASHLT
|
382
|
-
:T_SLASHLT2
|
384
|
+
:T_SLASH => :ACTION_CHILD,
|
385
|
+
:T_SLASH2 => :ACTION_DESCENDANT,
|
386
|
+
:T_SLASHDOT2 => :ACTION_PARENT,
|
387
|
+
:T_SLASHDOT3 => :ACTION_ANCESTOR,
|
388
|
+
:T_SLASHGT => :ACTION_FOLLOWING_SIBLING,
|
389
|
+
:T_SLASHGT2 => :ACTION_FOLLOWING,
|
390
|
+
:T_SLASHLT => :ACTION_PRECEDING_SIBLING,
|
391
|
+
:T_SLASHLT2 => :ACTION_PRECEDING
|
383
392
|
}
|
384
393
|
|
385
394
|
# Parse a Scandent string by giving it to the Tokenizer and then parsing the results
|
@@ -444,6 +453,7 @@ module Arboretum
|
|
444
453
|
raise ParseException.new("Unknown step type")
|
445
454
|
end
|
446
455
|
|
456
|
+
# Initialize all of the components that a step uses to match an element
|
447
457
|
element_ref = []
|
448
458
|
tag = []
|
449
459
|
namespace = []
|
@@ -457,124 +467,103 @@ module Arboretum
|
|
457
467
|
pseudo_exps = []
|
458
468
|
valid_rules = []
|
459
469
|
|
460
|
-
|
470
|
+
# Walk through the tokens of this step one at a time to parse grammar
|
471
|
+
index = -1
|
461
472
|
state = :STATE_ROOT_PATH
|
462
|
-
while index < step_tokens.length
|
463
|
-
# Consume
|
464
|
-
index_token = step_tokens[index]
|
465
|
-
index += 1
|
473
|
+
while (index + 1) < step_tokens.length
|
474
|
+
# Consume next token
|
475
|
+
index_token = step_tokens[index+=1]
|
466
476
|
|
467
477
|
case index_token[0]
|
468
478
|
when :T_PCT
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
tag << following_token[1]
|
479
|
+
next_token = step_tokens[index+=1]
|
480
|
+
raise InvalidExpressionException.new if next_token[0] != :LITERAL_IDENT
|
481
|
+
tag << next_token[1]
|
473
482
|
when :T_AT
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
namespace << following_token[1]
|
483
|
+
next_token = step_tokens[index+=1]
|
484
|
+
raise InvalidExpressionException.new if next_token[0] != :LITERAL_IDENT
|
485
|
+
namespace << next_token[1]
|
478
486
|
when :T_PND
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
id << following_token[1]
|
487
|
+
next_token = step_tokens[index+=1]
|
488
|
+
raise InvalidExpressionException.new if next_token[0] != :LITERAL_IDENT
|
489
|
+
id << next_token[1]
|
483
490
|
when :T_COLON
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
pseudo_name = following_token[1].to_sym
|
491
|
+
next_token = step_tokens[index+=1]
|
492
|
+
raise InvalidExpressionException.new if next_token[0] != :LITERAL_IDENT
|
493
|
+
pseudo_name = next_token[1].to_sym
|
488
494
|
arg_tokens = []
|
489
495
|
|
490
|
-
|
491
|
-
if
|
492
|
-
index
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
arg_tokens << following_token
|
497
|
-
following_token = step_tokens[index]
|
498
|
-
index += 1
|
496
|
+
next_token = step_tokens[index+=1]
|
497
|
+
if next_token[0] == :T_LPAREN
|
498
|
+
next_token = step_tokens[index+=1]
|
499
|
+
until next_token[0] == :T_RPAREN or index > step_tokens.length
|
500
|
+
arg_tokens << next_token
|
501
|
+
next_token = step_tokens[index+=1]
|
499
502
|
end
|
500
503
|
raise InvalidExpressionException.new if index > step_tokens.length # Undesirable exit condition to above loop
|
501
|
-
index += 1 # To consume the RPAREN
|
502
504
|
end
|
503
505
|
pseudo_exps << [pseudo_name, Parser.parse_arg(arg_tokens)]
|
504
|
-
when :T_ASTERISK
|
506
|
+
when :T_ASTERISK
|
507
|
+
# Adds no restrictions, so do nothing
|
505
508
|
when :T_TILDE
|
506
509
|
element_ref << :ELEMENT_ROOT
|
507
510
|
when :T_DOT
|
508
511
|
element_ref << :ELEMENT_SELF
|
509
512
|
when :T_LBRAK
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
attr_name = following_token[1].to_sym
|
513
|
+
next_token = step_tokens[index+=1]
|
514
|
+
raise InvalidExpressionException.new if next_token[0] != :LITERAL_IDENT
|
515
|
+
attr_name = next_token[1].to_sym
|
514
516
|
attr_value = nil
|
515
517
|
operation = nil
|
516
518
|
|
517
|
-
|
518
|
-
|
519
|
-
case following_token[0]
|
519
|
+
next_token = step_tokens[index+=1]
|
520
|
+
case next_token[0]
|
520
521
|
when :T_EQL
|
521
522
|
operation = :contains
|
522
523
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
string_limiter = following_token[0]
|
524
|
+
next_token = step_tokens[index+=1]
|
525
|
+
raise InvalidExpressionException.new("Expected a string after '='") if ![:T_DQUOTE, :T_SQUOTE].include?(next_token[0])
|
526
|
+
string_limiter = next_token[0]
|
527
527
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
attr_value = following_token[1]
|
528
|
+
next_token = step_tokens[index+=1]
|
529
|
+
raise InvalidExpressionException.new if not [:LITERAL_IDENT, :LITERAL_STRING, :LITERAL_INT, :LITERAL_FLOAT].include?(next_token[0])
|
530
|
+
attr_value = next_token[1]
|
532
531
|
|
533
|
-
|
534
|
-
|
535
|
-
raise InvalidExpressionException.new if following_token[0] != string_limiter
|
532
|
+
next_token = step_tokens[index+=1]
|
533
|
+
raise InvalidExpressionException.new if next_token[0] != string_limiter
|
536
534
|
|
537
|
-
|
538
|
-
|
539
|
-
raise InvalidExpressionException.new if following_token[0] != :T_RBRAK
|
535
|
+
next_token = step_tokens[index+=1]
|
536
|
+
raise InvalidExpressionException.new if next_token[0] != :T_RBRAK
|
540
537
|
when :T_EQL2
|
541
538
|
operation = :equals
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
index
|
554
|
-
raise InvalidExpressionException.new if
|
555
|
-
|
556
|
-
following_token = step_tokens[index]
|
557
|
-
index += 1
|
558
|
-
raise InvalidExpressionException.new if following_token[0] != :T_RBRAK
|
539
|
+
next_token = step_tokens[index+=1]
|
540
|
+
raise InvalidExpressionException.new if ![:T_DQUOTE, :T_SQUOTE].include?(next_token[0])
|
541
|
+
string_limiter = next_token[0]
|
542
|
+
|
543
|
+
next_token = step_tokens[index+=1]
|
544
|
+
raise InvalidExpressionException.new if not [:LITERAL_IDENT, :LITERAL_STRING, :LITERAL_INT, :LITERAL_FLOAT].include?(next_token[0])
|
545
|
+
attr_value = next_token[1].split
|
546
|
+
|
547
|
+
next_token = step_tokens[index+=1]
|
548
|
+
raise InvalidExpressionException.new if next_token[0] != string_limiter
|
549
|
+
|
550
|
+
next_token = step_tokens[index+=1]
|
551
|
+
raise InvalidExpressionException.new if next_token[0] != :T_RBRAK
|
559
552
|
when :T_TILDE_EQL
|
560
553
|
operation = :matches
|
561
554
|
|
562
|
-
|
563
|
-
|
564
|
-
raise InvalidExpressionException.new if following_token[0] != :T_VBARSLASH
|
555
|
+
next_token = step_tokens[index+=1]
|
556
|
+
raise InvalidExpressionException.new if next_token[0] != :T_VBARSLASH
|
565
557
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
attr_value = Regexp.new(following_token[1])
|
558
|
+
next_token = step_tokens[index+=1]
|
559
|
+
raise InvalidExpressionException.new if not [:LITERAL_IDENT, :LITERAL_STRING, :LITERAL_INT, :LITERAL_FLOAT].include?(next_token[0])
|
560
|
+
attr_value = Regexp.new(next_token[1])
|
570
561
|
|
571
|
-
|
572
|
-
|
573
|
-
raise InvalidExpressionException.new if following_token[0] != :T_SLASHVBAR
|
562
|
+
next_token = step_tokens[index+=1]
|
563
|
+
raise InvalidExpressionException.new if next_token[0] != :T_SLASHVBAR
|
574
564
|
|
575
|
-
|
576
|
-
|
577
|
-
raise InvalidExpressionException.new if following_token[0] != :T_RBRAK
|
565
|
+
next_token = step_tokens[index+=1]
|
566
|
+
raise InvalidExpressionException.new if next_token[0] != :T_RBRAK
|
578
567
|
when :T_RBRAK
|
579
568
|
operation = nil
|
580
569
|
else
|
@@ -588,18 +577,17 @@ module Arboretum
|
|
588
577
|
when :T_LBRACE
|
589
578
|
equilibrium = 1
|
590
579
|
reformed_path_string = ''
|
591
|
-
|
592
|
-
index
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
equilibrium += 1 if following_token[0] == :T_LBRACE
|
598
|
-
equilibrium -= 1 if following_token[0] == :T_RBRACE
|
580
|
+
next_token = step_tokens[index+=1]
|
581
|
+
until (next_token[0] == :T_RBRACE and equilibrium.zero?) or index > step_tokens.length
|
582
|
+
reformed_path_string << next_token[1]
|
583
|
+
next_token = step_tokens[index+=1]
|
584
|
+
equilibrium += 1 if next_token[0] == :T_LBRACE
|
585
|
+
equilibrium -= 1 if next_token[0] == :T_RBRACE
|
599
586
|
end
|
600
587
|
raise InvalidExpressionException.new("Could not find matching R_BRACE in #{reformed_path_string}") if index > step_tokens.length # Undesirable exit condition to above loop
|
601
588
|
valid_rules << Parser.parse_rule_string(reformed_path_string, :PATH_LOCATOR)
|
602
589
|
when :T_SLASH, :T_SLASH2, :T_SLASHDOT2, :T_SLASHDOT3, :T_SLASHGT, :T_SLASHGT2, :T_SLASHLT, :T_SLASHLT2
|
590
|
+
# Do nothing since the action has already been determined
|
603
591
|
else
|
604
592
|
raise ParseException.new("Consumed unexpected token: #{index_token}")
|
605
593
|
end
|
data/lib/arboretum/xml.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
+
require 'ox'
|
2
|
+
|
1
3
|
module Arboretum
|
2
4
|
module XML
|
3
5
|
module IO
|
4
|
-
require 'ox'
|
5
|
-
require_relative 'doctree'
|
6
|
-
|
7
6
|
class XMLParseException < StandardError
|
8
7
|
def initialize(msg="An error occurred while parsing XML")
|
9
8
|
super(msg)
|
@@ -87,7 +86,7 @@ module Arboretum
|
|
87
86
|
|
88
87
|
# Add TaggedElement to tree
|
89
88
|
opened_element = TaggedElement.new(element_ns, element_tag)
|
90
|
-
@open_elements.last
|
89
|
+
opened_element.graft_last_onto(@open_elements.last)
|
91
90
|
|
92
91
|
# Open the element if paired
|
93
92
|
@open_elements.push(opened_element)
|
@@ -101,7 +100,7 @@ module Arboretum
|
|
101
100
|
def comment(str)
|
102
101
|
# Add CommentElement to tree
|
103
102
|
comment_element = CommentElement.new(str)
|
104
|
-
@open_elements.last
|
103
|
+
comment_element.graft_last_onto(@open_elements.last)
|
105
104
|
# Do not open the element (no children)
|
106
105
|
end
|
107
106
|
def text(str)
|
@@ -111,13 +110,13 @@ module Arboretum
|
|
111
110
|
|
112
111
|
# Add TextElement to tree
|
113
112
|
text_element = TextElement.new(str)
|
114
|
-
@open_elements.last
|
113
|
+
text_element.graft_last_onto(@open_elements.last)
|
115
114
|
# Do not open the element (no children)
|
116
115
|
|
117
116
|
elsif @style == :preserve
|
118
117
|
# Add TextElement to tree
|
119
118
|
text_element = TextElement.new(str)
|
120
|
-
@open_elements.last
|
119
|
+
text_element.graft_last_onto(@open_elements.last)
|
121
120
|
# Do not open the element (no children)
|
122
121
|
end
|
123
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arboretum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tomjw64
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ox
|
@@ -37,6 +37,7 @@ extensions: []
|
|
37
37
|
extra_rdoc_files: []
|
38
38
|
files:
|
39
39
|
- lib/arboretum.rb
|
40
|
+
- lib/arboretum/debtor.rb
|
40
41
|
- lib/arboretum/doctree.rb
|
41
42
|
- lib/arboretum/scandent.rb
|
42
43
|
- lib/arboretum/xml.rb
|