arboretum 0.0.6 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|