parselly 1.1.0 → 1.2.0
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/parselly/lexer.rb +11 -4
- data/lib/parselly/node.rb +90 -5
- data/lib/parselly/parser.rb +240 -135
- data/lib/parselly/version.rb +1 -1
- data/lib/parselly.rb +16 -1
- data/parser.y +115 -17
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e1e4e245059433130b385388d399fe29355a1c679a15327a02cb3b49e69ae23b
|
|
4
|
+
data.tar.gz: ceb1167e8a32c25543b96988f754f76eb01fe287fc279a6ceb8dd26ffc258ca9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 33dab2f628019bbd51d482c53ae267f98914a5a561beb146ce57b3625f30535b661de168b2c6150faa79f16af8a49a8c15e7d6047a118577359fdd9334249a7f
|
|
7
|
+
data.tar.gz: 38803e8cc427a8eaa0b2a63f9446cf91334a368a7ea86908da80eb62cca3569a7b44f4c9536165a57f99048cf604dd948814b12dff25ac49c43ab637ec344324
|
data/lib/parselly/lexer.rb
CHANGED
|
@@ -4,6 +4,12 @@ require 'strscan'
|
|
|
4
4
|
|
|
5
5
|
module Parselly
|
|
6
6
|
class Lexer
|
|
7
|
+
Identifier = Struct.new(:value, :raw) do
|
|
8
|
+
def to_s
|
|
9
|
+
value
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
7
13
|
TOKENS = {
|
|
8
14
|
# Combinators
|
|
9
15
|
'>' => :CHILD,
|
|
@@ -62,7 +68,7 @@ module Parselly
|
|
|
62
68
|
skip_whitespace
|
|
63
69
|
break if @scanner.eos?
|
|
64
70
|
|
|
65
|
-
pos = { line: @line, column: @column }
|
|
71
|
+
pos = { line: @line, column: @column, offset: @scanner.pos }
|
|
66
72
|
|
|
67
73
|
if (token = scan_string)
|
|
68
74
|
@tokens << [:STRING, token, pos]
|
|
@@ -74,11 +80,11 @@ module Parselly
|
|
|
74
80
|
@tokens << [:IDENT, token, pos]
|
|
75
81
|
else
|
|
76
82
|
char = @scanner.getch
|
|
77
|
-
raise "Unexpected character: #{char} at #{pos[:line]}:#{pos[:column]}"
|
|
83
|
+
raise "Unexpected character: #{char} at #{pos[:line]}:#{pos[:column]} (offset #{pos[:offset]})"
|
|
78
84
|
end
|
|
79
85
|
end
|
|
80
86
|
|
|
81
|
-
@tokens << [false, nil, { line: @line, column: @column }]
|
|
87
|
+
@tokens << [false, nil, { line: @line, column: @column, offset: @scanner.pos }]
|
|
82
88
|
@tokens
|
|
83
89
|
end
|
|
84
90
|
|
|
@@ -145,7 +151,8 @@ module Parselly
|
|
|
145
151
|
ident = @scanner.matched
|
|
146
152
|
update_position(ident)
|
|
147
153
|
# Remove backslashes from escaped characters
|
|
148
|
-
ident.gsub(ESCAPE_REGEX, '\1')
|
|
154
|
+
normalized = ident.gsub(ESCAPE_REGEX, '\1')
|
|
155
|
+
Identifier.new(normalized, ident)
|
|
149
156
|
end
|
|
150
157
|
|
|
151
158
|
def scan_number
|
data/lib/parselly/node.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Parselly
|
|
|
8
8
|
# child nodes, parent reference, and source position.
|
|
9
9
|
#
|
|
10
10
|
# @example Creating a simple AST node
|
|
11
|
-
# node = Parselly::Node.new(:type_selector, 'div', { line: 1, column: 1 })
|
|
11
|
+
# node = Parselly::Node.new(:type_selector, 'div', { line: 1, column: 1, offset: 0 })
|
|
12
12
|
# node.add_child(Parselly::Node.new(:class_selector, 'container'))
|
|
13
13
|
#
|
|
14
14
|
# @example Traversing the AST
|
|
@@ -16,19 +16,31 @@ module Parselly
|
|
|
16
16
|
# node.descendants # Returns array of all descendant nodes
|
|
17
17
|
# node.siblings # Returns array of sibling nodes
|
|
18
18
|
class Node
|
|
19
|
-
attr_accessor :type, :value, :children, :parent, :position
|
|
19
|
+
attr_accessor :type, :value, :raw_value, :children, :parent, :position
|
|
20
20
|
|
|
21
21
|
# Creates a new AST node.
|
|
22
22
|
#
|
|
23
23
|
# @param type [Symbol] the type of the node (e.g., :type_selector, :class_selector)
|
|
24
24
|
# @param value [String, nil] optional value associated with the node
|
|
25
|
-
# @param position [Hash] source position with :line and :
|
|
26
|
-
|
|
25
|
+
# @param position [Hash] source position with :line, :column, and :offset keys
|
|
26
|
+
# @param line [Integer, nil] optional line number (keyword alternative)
|
|
27
|
+
# @param column [Integer, nil] optional column number (keyword alternative)
|
|
28
|
+
# @param offset [Integer, nil] optional offset (keyword alternative)
|
|
29
|
+
def initialize(type, value = nil, position = {}, raw_value: nil, line: nil, column: nil, offset: nil)
|
|
27
30
|
@type = type
|
|
28
31
|
@value = value
|
|
32
|
+
@raw_value = raw_value.nil? ? value : raw_value
|
|
29
33
|
@children = []
|
|
30
34
|
@parent = nil
|
|
31
|
-
|
|
35
|
+
unless position.nil? || position.is_a?(Hash)
|
|
36
|
+
raise ArgumentError, 'position must be a Hash'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
resolved_position = position ? position.dup : {}
|
|
40
|
+
resolved_position[:line] = line unless line.nil?
|
|
41
|
+
resolved_position[:column] = column unless column.nil?
|
|
42
|
+
resolved_position[:offset] = offset unless offset.nil?
|
|
43
|
+
@position = resolved_position
|
|
32
44
|
@descendants_cache = nil
|
|
33
45
|
end
|
|
34
46
|
|
|
@@ -92,6 +104,31 @@ module Parselly
|
|
|
92
104
|
@descendants_cache
|
|
93
105
|
end
|
|
94
106
|
|
|
107
|
+
# Depth-first traversal of this node and its descendants.
|
|
108
|
+
#
|
|
109
|
+
# @return [Enumerator, Node] enumerator if no block, otherwise self
|
|
110
|
+
def each
|
|
111
|
+
return enum_for(:each) unless block_given?
|
|
112
|
+
|
|
113
|
+
stack = [self]
|
|
114
|
+
until stack.empty?
|
|
115
|
+
node = stack.pop
|
|
116
|
+
yield node
|
|
117
|
+
children = node.children
|
|
118
|
+
stack.concat(children.reverse) if children && !children.empty?
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
self
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Finds all nodes of a given type in this subtree.
|
|
125
|
+
#
|
|
126
|
+
# @param type [Symbol] the node type to match
|
|
127
|
+
# @return [Array<Node>] array of matching nodes
|
|
128
|
+
def find_all(type)
|
|
129
|
+
each.with_object([]) { |node, acc| acc << node if node.type == type }
|
|
130
|
+
end
|
|
131
|
+
|
|
95
132
|
# Returns an array of sibling nodes (excluding self).
|
|
96
133
|
#
|
|
97
134
|
# @return [Array<Node>] array of sibling nodes, or empty array if no parent
|
|
@@ -230,6 +267,24 @@ module Parselly
|
|
|
230
267
|
result
|
|
231
268
|
end
|
|
232
269
|
|
|
270
|
+
# Extracts detailed attribute selector nodes from this node and its descendants.
|
|
271
|
+
#
|
|
272
|
+
# @return [Array<Hash>] array of attribute selector detail hashes
|
|
273
|
+
# Each hash contains :name, :operator (optional), and :value (optional) keys
|
|
274
|
+
def attribute_selectors
|
|
275
|
+
result = []
|
|
276
|
+
|
|
277
|
+
if type == :attribute_selector
|
|
278
|
+
result << extract_attribute_node(self)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
descendants.each do |node|
|
|
282
|
+
result << extract_attribute_node(node) if node.type == :attribute_selector
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
result
|
|
286
|
+
end
|
|
287
|
+
|
|
233
288
|
# Extracts all pseudo-classes and pseudo-elements from this node and its descendants.
|
|
234
289
|
#
|
|
235
290
|
# @return [Array<String>] array of pseudo-class and pseudo-element names
|
|
@@ -316,6 +371,36 @@ module Parselly
|
|
|
316
371
|
info
|
|
317
372
|
end
|
|
318
373
|
|
|
374
|
+
# Helper method to extract detailed attribute selector data.
|
|
375
|
+
#
|
|
376
|
+
# @param node [Node] an attribute_selector node
|
|
377
|
+
# @return [Hash] attribute selector detail hash
|
|
378
|
+
def extract_attribute_node(node)
|
|
379
|
+
info = {}
|
|
380
|
+
|
|
381
|
+
if node.value
|
|
382
|
+
info[:name] = node.value
|
|
383
|
+
info[:raw_name] = node.raw_value
|
|
384
|
+
return info
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
node.children.each do |child|
|
|
388
|
+
case child.type
|
|
389
|
+
when :attribute
|
|
390
|
+
info[:name] = child.value
|
|
391
|
+
info[:raw_name] = child.raw_value
|
|
392
|
+
when :equal_operator, :includes_operator, :dashmatch_operator,
|
|
393
|
+
:prefixmatch_operator, :suffixmatch_operator, :substringmatch_operator
|
|
394
|
+
info[:operator] = child.value
|
|
395
|
+
when :value
|
|
396
|
+
info[:value] = child.value
|
|
397
|
+
info[:raw_value] = child.raw_value
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
info
|
|
402
|
+
end
|
|
403
|
+
|
|
319
404
|
# Helper method to build an attribute selector string.
|
|
320
405
|
#
|
|
321
406
|
# @return [String] the attribute selector string
|
data/lib/parselly/parser.rb
CHANGED
|
@@ -660,24 +660,102 @@ CAN_END_COMPOUND = Set[:IDENT, :STAR, :RPAREN, :RBRACKET].freeze
|
|
|
660
660
|
CAN_START_COMPOUND = Set[:IDENT, :STAR, :DOT, :HASH, :LBRACKET, :COLON].freeze
|
|
661
661
|
TYPE_SELECTOR_TYPES = Set[:IDENT, :STAR].freeze
|
|
662
662
|
SUBCLASS_SELECTOR_TYPES = Set[:DOT, :HASH, :LBRACKET, :COLON].freeze
|
|
663
|
+
SUBCLASS_SELECTOR_END_TYPES = Set[:IDENT, :RBRACKET, :RPAREN].freeze
|
|
663
664
|
NTH_PSEUDO_NAMES = Set['nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type', 'nth-col', 'nth-last-col'].freeze
|
|
664
665
|
AN_PLUS_B_REGEX = /^(even|odd|[+-]?\d*n(?:[+-]\d+)?|[+-]?n(?:[+-]\d+)?|\d+)$/.freeze
|
|
665
666
|
|
|
666
667
|
module Parselly
|
|
667
668
|
class Parser < Racc::Parser
|
|
668
669
|
|
|
669
|
-
module_eval(<<'...end parser.y/module_eval...', 'parser.y',
|
|
670
|
-
def parse(input)
|
|
670
|
+
module_eval(<<'...end parser.y/module_eval...', 'parser.y', 279)
|
|
671
|
+
def parse(input, tolerant: false)
|
|
672
|
+
@tolerant = tolerant
|
|
673
|
+
@errors = []
|
|
674
|
+
@error_index = nil
|
|
675
|
+
@suppress_errors = false
|
|
671
676
|
@lexer = Parselly::Lexer.new(input)
|
|
672
|
-
|
|
677
|
+
begin
|
|
678
|
+
@tokens = @lexer.tokenize
|
|
679
|
+
rescue RuntimeError => e
|
|
680
|
+
if tolerant
|
|
681
|
+
@errors << parse_error_from_exception(e)
|
|
682
|
+
return Parselly::ParseResult.new(nil, @errors)
|
|
683
|
+
end
|
|
684
|
+
raise
|
|
685
|
+
end
|
|
673
686
|
preprocess_tokens!
|
|
674
687
|
@index = 0
|
|
675
|
-
@current_position = { line: 1, column: 1 }
|
|
688
|
+
@current_position = { line: 1, column: 1, offset: 0 }
|
|
689
|
+
|
|
690
|
+
if tolerant
|
|
691
|
+
ast = parse_with_recovery
|
|
692
|
+
normalize_an_plus_b(ast) if ast
|
|
693
|
+
return Parselly::ParseResult.new(ast, @errors)
|
|
694
|
+
end
|
|
695
|
+
|
|
676
696
|
ast = do_parse
|
|
677
697
|
normalize_an_plus_b(ast)
|
|
678
698
|
ast
|
|
679
699
|
end
|
|
680
700
|
|
|
701
|
+
def parse_with_recovery
|
|
702
|
+
do_parse
|
|
703
|
+
rescue Parselly::ParseError, RuntimeError
|
|
704
|
+
parse_partial_ast
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def parse_partial_ast
|
|
708
|
+
return nil unless @tokens && !@tokens.empty?
|
|
709
|
+
|
|
710
|
+
eof_token = @tokens.last if @tokens.last && @tokens.last[0] == false
|
|
711
|
+
tokens = @tokens.dup
|
|
712
|
+
tokens.pop if eof_token
|
|
713
|
+
limit = @error_index || tokens.length
|
|
714
|
+
|
|
715
|
+
while limit > 0
|
|
716
|
+
truncated = tokens[0...limit]
|
|
717
|
+
truncated << eof_token if eof_token
|
|
718
|
+
begin
|
|
719
|
+
return parse_from_tokens(truncated, suppress_errors: true)
|
|
720
|
+
rescue Parselly::ParseError, RuntimeError
|
|
721
|
+
limit -= 1
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
nil
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
def parse_from_tokens(tokens, suppress_errors: false)
|
|
728
|
+
@tokens = tokens
|
|
729
|
+
@index = 0
|
|
730
|
+
@current_position = { line: 1, column: 1, offset: 0 }
|
|
731
|
+
@suppress_errors = suppress_errors
|
|
732
|
+
do_parse
|
|
733
|
+
ensure
|
|
734
|
+
@suppress_errors = false
|
|
735
|
+
end
|
|
736
|
+
|
|
737
|
+
def parse_error_from_exception(error)
|
|
738
|
+
line = nil
|
|
739
|
+
column = nil
|
|
740
|
+
offset = nil
|
|
741
|
+
if error.message =~ /at (\d+):(\d+)/
|
|
742
|
+
line = Regexp.last_match(1).to_i
|
|
743
|
+
column = Regexp.last_match(2).to_i
|
|
744
|
+
end
|
|
745
|
+
if error.message =~ /offset (\d+)/
|
|
746
|
+
offset = Regexp.last_match(1).to_i
|
|
747
|
+
end
|
|
748
|
+
{ message: error.message, line: line, column: column, offset: offset }
|
|
749
|
+
end
|
|
750
|
+
|
|
751
|
+
def identifier_value(token)
|
|
752
|
+
token.respond_to?(:value) ? token.value : token
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
def identifier_raw(token)
|
|
756
|
+
token.respond_to?(:raw) ? token.raw : token
|
|
757
|
+
end
|
|
758
|
+
|
|
681
759
|
def preprocess_tokens!
|
|
682
760
|
return if @tokens.size <= 1
|
|
683
761
|
|
|
@@ -692,7 +770,7 @@ def preprocess_tokens!
|
|
|
692
770
|
if i < last_idx
|
|
693
771
|
next_token = @tokens[i + 1]
|
|
694
772
|
if needs_descendant?(token, next_token)
|
|
695
|
-
pos = { line: token[2][:line], column: token[2][:column] }
|
|
773
|
+
pos = { line: token[2][:line], column: token[2][:column], offset: token[2][:offset] }
|
|
696
774
|
new_tokens[new_tokens_idx] = [:DESCENDANT, ' ', pos]
|
|
697
775
|
new_tokens_idx += 1
|
|
698
776
|
end
|
|
@@ -712,8 +790,11 @@ def needs_descendant?(current, next_tok)
|
|
|
712
790
|
next_type = next_tok[0]
|
|
713
791
|
|
|
714
792
|
# Type selector followed by subclass selector = same compound
|
|
715
|
-
|
|
716
|
-
|
|
793
|
+
# Subclass selector followed by subclass selector = same compound
|
|
794
|
+
if SUBCLASS_SELECTOR_TYPES.include?(next_type)
|
|
795
|
+
return false if TYPE_SELECTOR_TYPES.include?(current_type) ||
|
|
796
|
+
SUBCLASS_SELECTOR_END_TYPES.include?(current_type)
|
|
797
|
+
end
|
|
717
798
|
|
|
718
799
|
CAN_END_COMPOUND.include?(current_type) && CAN_START_COMPOUND.include?(next_type)
|
|
719
800
|
end
|
|
@@ -759,88 +840,101 @@ end
|
|
|
759
840
|
def on_error(token_id, val, vstack)
|
|
760
841
|
token_name = token_to_str(token_id) || '?'
|
|
761
842
|
pos = @current_position || { line: '?', column: '?' }
|
|
762
|
-
|
|
843
|
+
error = {
|
|
844
|
+
message: "Parse error: unexpected #{token_name} '#{val}' at #{pos[:line]}:#{pos[:column]}",
|
|
845
|
+
line: pos[:line],
|
|
846
|
+
column: pos[:column],
|
|
847
|
+
offset: pos[:offset]
|
|
848
|
+
}
|
|
849
|
+
if @tolerant
|
|
850
|
+
@errors << error unless @suppress_errors
|
|
851
|
+
@error_index ||= [@index - 1, 0].max
|
|
852
|
+
raise Parselly::ParseError, error
|
|
853
|
+
end
|
|
854
|
+
raise error[:message]
|
|
763
855
|
end
|
|
764
856
|
...end parser.y/module_eval...
|
|
765
857
|
##### State transition tables begin ###
|
|
766
858
|
|
|
767
859
|
racc_action_table = [
|
|
768
|
-
|
|
769
|
-
17,
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
860
|
+
45, 47, 50, 14, 15, 8, 16, 55, 18, 70,
|
|
861
|
+
17, 69, 25, 26, 27, 28, 57, 58, 59, 60,
|
|
862
|
+
61, 62, 51, 45, 47, 50, 14, 15, 8, 16,
|
|
863
|
+
37, 19, 80, 17, 33, 25, 26, 27, 28, 34,
|
|
864
|
+
38, 81, 83, 7, 35, 51, 14, 15, 8, 16,
|
|
865
|
+
36, 84, 92, 17, 33, 25, 26, 27, 28, 65,
|
|
866
|
+
7, 93, 39, 14, 15, 8, 16, 19, 66, 32,
|
|
867
|
+
17, 33, 14, 15, 63, 16, 64, 7, 67, 17,
|
|
868
|
+
14, 15, 8, 16, 68, 7, 71, 17, 14, 15,
|
|
869
|
+
8, 16, 78, 32, 79, 17, 14, 15, 82, 16,
|
|
870
|
+
71, 7, 87, 17, 14, 15, 8, 16, 76, 75,
|
|
871
|
+
88, 17, 25, 26, 27, 28, 25, 26, 27, 28,
|
|
872
|
+
89, 90, 91, 94, 95, 96, 97 ]
|
|
779
873
|
|
|
780
874
|
racc_action_check = [
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
875
|
+
33, 33, 33, 33, 33, 33, 33, 36, 1, 51,
|
|
876
|
+
33, 51, 33, 33, 33, 33, 36, 36, 36, 36,
|
|
877
|
+
36, 36, 33, 63, 63, 63, 63, 63, 63, 63,
|
|
878
|
+
17, 2, 68, 63, 7, 63, 63, 63, 63, 14,
|
|
879
|
+
17, 68, 70, 71, 15, 63, 71, 71, 71, 71,
|
|
880
|
+
16, 70, 82, 71, 45, 71, 71, 71, 71, 45,
|
|
881
|
+
0, 82, 18, 0, 0, 0, 0, 20, 45, 4,
|
|
882
|
+
0, 32, 4, 4, 37, 4, 38, 19, 46, 4,
|
|
883
|
+
19, 19, 19, 19, 50, 22, 52, 19, 22, 22,
|
|
884
|
+
22, 22, 65, 30, 66, 22, 30, 30, 69, 30,
|
|
885
|
+
72, 54, 75, 30, 54, 54, 54, 54, 56, 56,
|
|
886
|
+
76, 54, 3, 3, 3, 3, 23, 23, 23, 23,
|
|
887
|
+
77, 80, 81, 83, 84, 92, 93 ]
|
|
792
888
|
|
|
793
889
|
racc_action_pointer = [
|
|
794
|
-
|
|
795
|
-
nil, nil, nil, nil,
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
nil, nil, nil, nil,
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
nil, nil, nil, nil ]
|
|
890
|
+
58, 8, 18, 98, 67, nil, nil, 24, nil, nil,
|
|
891
|
+
nil, nil, nil, nil, 37, 42, 48, 28, 62, 75,
|
|
892
|
+
54, nil, 83, 102, nil, nil, nil, nil, nil, nil,
|
|
893
|
+
91, nil, 61, -2, nil, nil, -2, 64, 74, nil,
|
|
894
|
+
nil, nil, nil, nil, nil, 44, 67, nil, nil, nil,
|
|
895
|
+
82, 7, 73, nil, 99, nil, 106, nil, nil, nil,
|
|
896
|
+
nil, nil, nil, 21, nil, 88, 90, nil, 17, 96,
|
|
897
|
+
27, 41, 87, nil, nil, 93, 101, 109, nil, nil,
|
|
898
|
+
117, 118, 37, 119, 120, nil, nil, nil, nil, nil,
|
|
899
|
+
nil, nil, 121, 122, nil, nil, nil, nil ]
|
|
804
900
|
|
|
805
901
|
racc_action_default = [
|
|
806
|
-
-
|
|
807
|
-
-22, -23, -24, -25, -
|
|
808
|
-
-2, -4, -
|
|
809
|
-
-16, -18, -26, -27, -
|
|
810
|
-
-
|
|
811
|
-
-
|
|
812
|
-
-
|
|
813
|
-
-
|
|
814
|
-
-
|
|
815
|
-
-
|
|
902
|
+
-62, -62, -2, -6, -16, -14, -15, -19, -20, -21,
|
|
903
|
+
-22, -23, -24, -25, -62, -62, -62, -62, -62, -62,
|
|
904
|
+
-2, -4, -62, -6, -8, -9, -10, -11, -12, -13,
|
|
905
|
+
-16, -18, -62, -62, -26, -27, -62, -37, -62, 98,
|
|
906
|
+
-1, -3, -5, -7, -17, -19, -62, -41, -42, -43,
|
|
907
|
+
-55, -62, -57, -60, -62, -28, -62, -31, -32, -33,
|
|
908
|
+
-34, -35, -36, -62, -40, -62, -62, -39, -46, -62,
|
|
909
|
+
-54, -62, -57, -59, -61, -62, -62, -62, -47, -48,
|
|
910
|
+
-62, -62, -51, -62, -62, -56, -58, -29, -30, -38,
|
|
911
|
+
-44, -45, -62, -62, -52, -53, -49, -50 ]
|
|
816
912
|
|
|
817
913
|
racc_goto_table = [
|
|
818
|
-
2, 30, 31,
|
|
819
|
-
29,
|
|
820
|
-
nil, nil, nil,
|
|
821
|
-
nil,
|
|
914
|
+
2, 46, 30, 31, 22, 24, 73, 1, 42, 21,
|
|
915
|
+
29, 56, 85, nil, nil, nil, nil, nil, nil, 40,
|
|
916
|
+
nil, nil, nil, nil, 22, 43, 86, 41, 30, 44,
|
|
917
|
+
nil, 77, nil, nil, nil, nil, nil, nil, nil, nil,
|
|
822
918
|
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
|
823
|
-
nil, nil, nil, nil,
|
|
824
|
-
nil, nil, nil, nil, 76 ]
|
|
919
|
+
nil, nil, nil, nil, 74 ]
|
|
825
920
|
|
|
826
921
|
racc_goto_check = [
|
|
827
|
-
2, 12, 13, 6,
|
|
828
|
-
10, 19,
|
|
829
|
-
nil, nil, nil,
|
|
830
|
-
nil,
|
|
831
|
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
|
922
|
+
2, 20, 12, 13, 6, 8, 25, 1, 5, 4,
|
|
923
|
+
10, 19, 23, nil, nil, nil, nil, nil, nil, 2,
|
|
924
|
+
nil, nil, nil, nil, 6, 8, 25, 4, 12, 13,
|
|
925
|
+
nil, 20, nil, nil, nil, nil, nil, nil, nil, nil,
|
|
832
926
|
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
|
|
833
927
|
nil, nil, nil, nil, 2 ]
|
|
834
928
|
|
|
835
929
|
racc_goto_pointer = [
|
|
836
|
-
nil, 7, 0, nil,
|
|
837
|
-
6, nil, -
|
|
838
|
-
-
|
|
930
|
+
nil, 7, 0, nil, 7, -14, 1, nil, 2, nil,
|
|
931
|
+
6, nil, -2, -1, nil, nil, nil, nil, nil, -25,
|
|
932
|
+
-32, nil, nil, -59, nil, -46 ]
|
|
839
933
|
|
|
840
934
|
racc_goto_default = [
|
|
841
|
-
nil, nil,
|
|
935
|
+
nil, nil, 53, 20, nil, 3, 54, 23, nil, 4,
|
|
842
936
|
nil, 5, 6, nil, 9, 10, 11, 12, 13, nil,
|
|
843
|
-
nil,
|
|
937
|
+
nil, 48, 49, 52, 72, nil ]
|
|
844
938
|
|
|
845
939
|
racc_reduce_table = [
|
|
846
940
|
0, 0, :racc_error,
|
|
@@ -882,32 +976,33 @@ racc_reduce_table = [
|
|
|
882
976
|
1, 45, :_reduce_36,
|
|
883
977
|
2, 43, :_reduce_37,
|
|
884
978
|
5, 43, :_reduce_38,
|
|
885
|
-
|
|
886
|
-
|
|
979
|
+
4, 43, :_reduce_39,
|
|
980
|
+
3, 44, :_reduce_40,
|
|
887
981
|
1, 46, :_reduce_41,
|
|
888
982
|
1, 46, :_reduce_42,
|
|
889
|
-
|
|
983
|
+
1, 46, :_reduce_43,
|
|
890
984
|
4, 47, :_reduce_44,
|
|
891
|
-
|
|
892
|
-
|
|
985
|
+
4, 47, :_reduce_45,
|
|
986
|
+
2, 47, :_reduce_46,
|
|
893
987
|
3, 47, :_reduce_47,
|
|
894
|
-
|
|
988
|
+
3, 47, :_reduce_48,
|
|
895
989
|
5, 47, :_reduce_49,
|
|
896
|
-
|
|
897
|
-
|
|
990
|
+
5, 47, :_reduce_50,
|
|
991
|
+
3, 47, :_reduce_51,
|
|
898
992
|
4, 47, :_reduce_52,
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
2,
|
|
905
|
-
|
|
906
|
-
|
|
993
|
+
4, 47, :_reduce_53,
|
|
994
|
+
2, 47, :_reduce_54,
|
|
995
|
+
1, 47, :_reduce_55,
|
|
996
|
+
2, 50, :_reduce_56,
|
|
997
|
+
0, 51, :_reduce_57,
|
|
998
|
+
2, 51, :_reduce_58,
|
|
999
|
+
2, 48, :_reduce_59,
|
|
1000
|
+
1, 49, :_reduce_60,
|
|
1001
|
+
2, 49, :_reduce_61 ]
|
|
907
1002
|
|
|
908
|
-
racc_reduce_n =
|
|
1003
|
+
racc_reduce_n = 62
|
|
909
1004
|
|
|
910
|
-
racc_shift_n =
|
|
1005
|
+
racc_shift_n = 98
|
|
911
1006
|
|
|
912
1007
|
racc_token_table = {
|
|
913
1008
|
false => 0,
|
|
@@ -1165,7 +1260,7 @@ module_eval(<<'.,.,', 'parser.y', 79)
|
|
|
1165
1260
|
|
|
1166
1261
|
module_eval(<<'.,.,', 'parser.y', 84)
|
|
1167
1262
|
def _reduce_19(val, _values, result)
|
|
1168
|
-
result = Node.new(:type_selector, val[0], @current_position)
|
|
1263
|
+
result = Node.new(:type_selector, identifier_value(val[0]), @current_position, raw_value: identifier_raw(val[0]))
|
|
1169
1264
|
result
|
|
1170
1265
|
end
|
|
1171
1266
|
.,.,
|
|
@@ -1214,21 +1309,21 @@ module_eval(<<'.,.,', 'parser.y', 99)
|
|
|
1214
1309
|
|
|
1215
1310
|
module_eval(<<'.,.,', 'parser.y', 104)
|
|
1216
1311
|
def _reduce_26(val, _values, result)
|
|
1217
|
-
result = Node.new(:id_selector, val[1], @current_position)
|
|
1312
|
+
result = Node.new(:id_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
1218
1313
|
result
|
|
1219
1314
|
end
|
|
1220
1315
|
.,.,
|
|
1221
1316
|
|
|
1222
1317
|
module_eval(<<'.,.,', 'parser.y', 109)
|
|
1223
1318
|
def _reduce_27(val, _values, result)
|
|
1224
|
-
result = Node.new(:class_selector, val[1], @current_position)
|
|
1319
|
+
result = Node.new(:class_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
1225
1320
|
result
|
|
1226
1321
|
end
|
|
1227
1322
|
.,.,
|
|
1228
1323
|
|
|
1229
1324
|
module_eval(<<'.,.,', 'parser.y', 114)
|
|
1230
1325
|
def _reduce_28(val, _values, result)
|
|
1231
|
-
result = Node.new(:attribute_selector, val[1], @current_position)
|
|
1326
|
+
result = Node.new(:attribute_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
1232
1327
|
result
|
|
1233
1328
|
end
|
|
1234
1329
|
.,.,
|
|
@@ -1236,7 +1331,7 @@ module_eval(<<'.,.,', 'parser.y', 114)
|
|
|
1236
1331
|
module_eval(<<'.,.,', 'parser.y', 117)
|
|
1237
1332
|
def _reduce_29(val, _values, result)
|
|
1238
1333
|
result = Node.new(:attribute_selector, nil, @current_position)
|
|
1239
|
-
result.add_child(Node.new(:attribute, val[1], @current_position))
|
|
1334
|
+
result.add_child(Node.new(:attribute, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])))
|
|
1240
1335
|
result.add_child(val[2])
|
|
1241
1336
|
result.add_child(Node.new(:value, val[3], @current_position))
|
|
1242
1337
|
|
|
@@ -1247,9 +1342,9 @@ module_eval(<<'.,.,', 'parser.y', 117)
|
|
|
1247
1342
|
module_eval(<<'.,.,', 'parser.y', 124)
|
|
1248
1343
|
def _reduce_30(val, _values, result)
|
|
1249
1344
|
result = Node.new(:attribute_selector, nil, @current_position)
|
|
1250
|
-
result.add_child(Node.new(:attribute, val[1], @current_position))
|
|
1345
|
+
result.add_child(Node.new(:attribute, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])))
|
|
1251
1346
|
result.add_child(val[2])
|
|
1252
|
-
result.add_child(Node.new(:value, val[3], @current_position))
|
|
1347
|
+
result.add_child(Node.new(:value, identifier_value(val[3]), @current_position, raw_value: identifier_raw(val[3])))
|
|
1253
1348
|
|
|
1254
1349
|
result
|
|
1255
1350
|
end
|
|
@@ -1299,14 +1394,14 @@ module_eval(<<'.,.,', 'parser.y', 143)
|
|
|
1299
1394
|
|
|
1300
1395
|
module_eval(<<'.,.,', 'parser.y', 148)
|
|
1301
1396
|
def _reduce_37(val, _values, result)
|
|
1302
|
-
result = Node.new(:pseudo_class, val[1], @current_position)
|
|
1397
|
+
result = Node.new(:pseudo_class, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
1303
1398
|
result
|
|
1304
1399
|
end
|
|
1305
1400
|
.,.,
|
|
1306
1401
|
|
|
1307
1402
|
module_eval(<<'.,.,', 'parser.y', 151)
|
|
1308
1403
|
def _reduce_38(val, _values, result)
|
|
1309
|
-
fn = Node.new(:pseudo_function, val[1], @current_position)
|
|
1404
|
+
fn = Node.new(:pseudo_function, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
1310
1405
|
fn.add_child(val[3])
|
|
1311
1406
|
result = fn
|
|
1312
1407
|
|
|
@@ -1314,36 +1409,46 @@ module_eval(<<'.,.,', 'parser.y', 151)
|
|
|
1314
1409
|
end
|
|
1315
1410
|
.,.,
|
|
1316
1411
|
|
|
1317
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1412
|
+
module_eval(<<'.,.,', 'parser.y', 157)
|
|
1318
1413
|
def _reduce_39(val, _values, result)
|
|
1319
|
-
|
|
1414
|
+
fn = Node.new(:pseudo_function, identifier_value(val[0]), @current_position, raw_value: identifier_raw(val[0]))
|
|
1415
|
+
fn.add_child(val[2])
|
|
1416
|
+
result = fn
|
|
1417
|
+
|
|
1320
1418
|
result
|
|
1321
1419
|
end
|
|
1322
1420
|
.,.,
|
|
1323
1421
|
|
|
1324
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1422
|
+
module_eval(<<'.,.,', 'parser.y', 165)
|
|
1325
1423
|
def _reduce_40(val, _values, result)
|
|
1326
|
-
result = Node.new(:
|
|
1424
|
+
result = Node.new(:pseudo_element, identifier_value(val[2]), @current_position, raw_value: identifier_raw(val[2]))
|
|
1327
1425
|
result
|
|
1328
1426
|
end
|
|
1329
1427
|
.,.,
|
|
1330
1428
|
|
|
1331
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1429
|
+
module_eval(<<'.,.,', 'parser.y', 170)
|
|
1332
1430
|
def _reduce_41(val, _values, result)
|
|
1333
|
-
result = val[0]
|
|
1431
|
+
result = Node.new(:argument, val[0], @current_position)
|
|
1334
1432
|
result
|
|
1335
1433
|
end
|
|
1336
1434
|
.,.,
|
|
1337
1435
|
|
|
1338
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1436
|
+
module_eval(<<'.,.,', 'parser.y', 172)
|
|
1339
1437
|
def _reduce_42(val, _values, result)
|
|
1340
1438
|
result = val[0]
|
|
1341
1439
|
result
|
|
1342
1440
|
end
|
|
1343
1441
|
.,.,
|
|
1344
1442
|
|
|
1345
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1443
|
+
module_eval(<<'.,.,', 'parser.y', 174)
|
|
1346
1444
|
def _reduce_43(val, _values, result)
|
|
1445
|
+
result = val[0]
|
|
1446
|
+
result
|
|
1447
|
+
end
|
|
1448
|
+
.,.,
|
|
1449
|
+
|
|
1450
|
+
module_eval(<<'.,.,', 'parser.y', 181)
|
|
1451
|
+
def _reduce_44(val, _values, result)
|
|
1347
1452
|
# Handle 'An+B' like '2n+1'
|
|
1348
1453
|
result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}+#{val[3]}", @current_position)
|
|
1349
1454
|
|
|
@@ -1351,8 +1456,8 @@ module_eval(<<'.,.,', 'parser.y', 175)
|
|
|
1351
1456
|
end
|
|
1352
1457
|
.,.,
|
|
1353
1458
|
|
|
1354
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1355
|
-
def
|
|
1459
|
+
module_eval(<<'.,.,', 'parser.y', 186)
|
|
1460
|
+
def _reduce_45(val, _values, result)
|
|
1356
1461
|
# Handle 'An-B' like '2n-1'
|
|
1357
1462
|
result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}-#{val[3]}", @current_position)
|
|
1358
1463
|
|
|
@@ -1360,8 +1465,8 @@ module_eval(<<'.,.,', 'parser.y', 180)
|
|
|
1360
1465
|
end
|
|
1361
1466
|
.,.,
|
|
1362
1467
|
|
|
1363
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1364
|
-
def
|
|
1468
|
+
module_eval(<<'.,.,', 'parser.y', 191)
|
|
1469
|
+
def _reduce_46(val, _values, result)
|
|
1365
1470
|
# Handle 'An' like '2n' or composite like '2n-1' (when '-1' is part of IDENT)
|
|
1366
1471
|
result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}", @current_position)
|
|
1367
1472
|
|
|
@@ -1369,8 +1474,8 @@ module_eval(<<'.,.,', 'parser.y', 185)
|
|
|
1369
1474
|
end
|
|
1370
1475
|
.,.,
|
|
1371
1476
|
|
|
1372
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1373
|
-
def
|
|
1477
|
+
module_eval(<<'.,.,', 'parser.y', 196)
|
|
1478
|
+
def _reduce_47(val, _values, result)
|
|
1374
1479
|
# Handle 'n+B' like 'n+5' or keywords followed by offset (rare but valid)
|
|
1375
1480
|
result = Node.new(:an_plus_b, "#{val[0]}+#{val[2]}", @current_position)
|
|
1376
1481
|
|
|
@@ -1378,8 +1483,8 @@ module_eval(<<'.,.,', 'parser.y', 190)
|
|
|
1378
1483
|
end
|
|
1379
1484
|
.,.,
|
|
1380
1485
|
|
|
1381
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1382
|
-
def
|
|
1486
|
+
module_eval(<<'.,.,', 'parser.y', 201)
|
|
1487
|
+
def _reduce_48(val, _values, result)
|
|
1383
1488
|
# Handle 'n-B' like 'n-3'
|
|
1384
1489
|
result = Node.new(:an_plus_b, "#{val[0]}-#{val[2]}", @current_position)
|
|
1385
1490
|
|
|
@@ -1387,8 +1492,8 @@ module_eval(<<'.,.,', 'parser.y', 195)
|
|
|
1387
1492
|
end
|
|
1388
1493
|
.,.,
|
|
1389
1494
|
|
|
1390
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1391
|
-
def
|
|
1495
|
+
module_eval(<<'.,.,', 'parser.y', 207)
|
|
1496
|
+
def _reduce_49(val, _values, result)
|
|
1392
1497
|
# Handle '-An+B' like '-2n+1'
|
|
1393
1498
|
result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}+#{val[4]}", @current_position)
|
|
1394
1499
|
|
|
@@ -1396,8 +1501,8 @@ module_eval(<<'.,.,', 'parser.y', 201)
|
|
|
1396
1501
|
end
|
|
1397
1502
|
.,.,
|
|
1398
1503
|
|
|
1399
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1400
|
-
def
|
|
1504
|
+
module_eval(<<'.,.,', 'parser.y', 212)
|
|
1505
|
+
def _reduce_50(val, _values, result)
|
|
1401
1506
|
# Handle '-An-B' like '-2n-1'
|
|
1402
1507
|
result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}-#{val[4]}", @current_position)
|
|
1403
1508
|
|
|
@@ -1405,8 +1510,8 @@ module_eval(<<'.,.,', 'parser.y', 206)
|
|
|
1405
1510
|
end
|
|
1406
1511
|
.,.,
|
|
1407
1512
|
|
|
1408
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1409
|
-
def
|
|
1513
|
+
module_eval(<<'.,.,', 'parser.y', 217)
|
|
1514
|
+
def _reduce_51(val, _values, result)
|
|
1410
1515
|
# Handle '-An' like '-2n' or composite like '-2n+1' (when '+1' is part of IDENT)
|
|
1411
1516
|
result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}", @current_position)
|
|
1412
1517
|
|
|
@@ -1414,8 +1519,8 @@ module_eval(<<'.,.,', 'parser.y', 211)
|
|
|
1414
1519
|
end
|
|
1415
1520
|
.,.,
|
|
1416
1521
|
|
|
1417
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1418
|
-
def
|
|
1522
|
+
module_eval(<<'.,.,', 'parser.y', 222)
|
|
1523
|
+
def _reduce_52(val, _values, result)
|
|
1419
1524
|
# Handle '-n+B' like '-n+3'
|
|
1420
1525
|
result = Node.new(:an_plus_b, "-#{val[1]}+#{val[3]}", @current_position)
|
|
1421
1526
|
|
|
@@ -1423,8 +1528,8 @@ module_eval(<<'.,.,', 'parser.y', 216)
|
|
|
1423
1528
|
end
|
|
1424
1529
|
.,.,
|
|
1425
1530
|
|
|
1426
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1427
|
-
def
|
|
1531
|
+
module_eval(<<'.,.,', 'parser.y', 227)
|
|
1532
|
+
def _reduce_53(val, _values, result)
|
|
1428
1533
|
# Handle '-n-B' like '-n-2'
|
|
1429
1534
|
result = Node.new(:an_plus_b, "-#{val[1]}-#{val[3]}", @current_position)
|
|
1430
1535
|
|
|
@@ -1432,8 +1537,8 @@ module_eval(<<'.,.,', 'parser.y', 221)
|
|
|
1432
1537
|
end
|
|
1433
1538
|
.,.,
|
|
1434
1539
|
|
|
1435
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1436
|
-
def
|
|
1540
|
+
module_eval(<<'.,.,', 'parser.y', 232)
|
|
1541
|
+
def _reduce_54(val, _values, result)
|
|
1437
1542
|
# Handle '-n' or composite like '-n+3' (when '+3' is part of IDENT)
|
|
1438
1543
|
result = Node.new(:an_plus_b, "-#{val[1]}", @current_position)
|
|
1439
1544
|
|
|
@@ -1441,8 +1546,8 @@ module_eval(<<'.,.,', 'parser.y', 226)
|
|
|
1441
1546
|
end
|
|
1442
1547
|
.,.,
|
|
1443
1548
|
|
|
1444
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1445
|
-
def
|
|
1549
|
+
module_eval(<<'.,.,', 'parser.y', 238)
|
|
1550
|
+
def _reduce_55(val, _values, result)
|
|
1446
1551
|
# Handle just a number like '3'
|
|
1447
1552
|
result = Node.new(:an_plus_b, val[0].to_s, @current_position)
|
|
1448
1553
|
|
|
@@ -1450,29 +1555,29 @@ module_eval(<<'.,.,', 'parser.y', 232)
|
|
|
1450
1555
|
end
|
|
1451
1556
|
.,.,
|
|
1452
1557
|
|
|
1453
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1454
|
-
def
|
|
1558
|
+
module_eval(<<'.,.,', 'parser.y', 251)
|
|
1559
|
+
def _reduce_56(val, _values, result)
|
|
1455
1560
|
result = val
|
|
1456
1561
|
result
|
|
1457
1562
|
end
|
|
1458
1563
|
.,.,
|
|
1459
1564
|
|
|
1460
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1461
|
-
def
|
|
1565
|
+
module_eval(<<'.,.,', 'parser.y', 251)
|
|
1566
|
+
def _reduce_57(val, _values, result)
|
|
1462
1567
|
result = val[1] ? val[1].unshift(val[0]) : val
|
|
1463
1568
|
result
|
|
1464
1569
|
end
|
|
1465
1570
|
.,.,
|
|
1466
1571
|
|
|
1467
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1468
|
-
def
|
|
1572
|
+
module_eval(<<'.,.,', 'parser.y', 251)
|
|
1573
|
+
def _reduce_58(val, _values, result)
|
|
1469
1574
|
result = val[1] ? val[1].unshift(val[0]) : val
|
|
1470
1575
|
result
|
|
1471
1576
|
end
|
|
1472
1577
|
.,.,
|
|
1473
1578
|
|
|
1474
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1475
|
-
def
|
|
1579
|
+
module_eval(<<'.,.,', 'parser.y', 246)
|
|
1580
|
+
def _reduce_59(val, _values, result)
|
|
1476
1581
|
result = Node.new(:selector_list, nil, @current_position)
|
|
1477
1582
|
result.add_child(val[0])
|
|
1478
1583
|
val[1].each { |pair| result.add_child(pair[1]) }
|
|
@@ -1481,15 +1586,15 @@ module_eval(<<'.,.,', 'parser.y', 240)
|
|
|
1481
1586
|
end
|
|
1482
1587
|
.,.,
|
|
1483
1588
|
|
|
1484
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1485
|
-
def
|
|
1589
|
+
module_eval(<<'.,.,', 'parser.y', 254)
|
|
1590
|
+
def _reduce_60(val, _values, result)
|
|
1486
1591
|
result = val[0]
|
|
1487
1592
|
result
|
|
1488
1593
|
end
|
|
1489
1594
|
.,.,
|
|
1490
1595
|
|
|
1491
|
-
module_eval(<<'.,.,', 'parser.y',
|
|
1492
|
-
def
|
|
1596
|
+
module_eval(<<'.,.,', 'parser.y', 257)
|
|
1597
|
+
def _reduce_61(val, _values, result)
|
|
1493
1598
|
result = Node.new(:selector, nil, val[0].position)
|
|
1494
1599
|
result.add_child(val[0])
|
|
1495
1600
|
result.add_child(val[1])
|
data/lib/parselly/version.rb
CHANGED
data/lib/parselly.rb
CHANGED
|
@@ -8,6 +8,21 @@ require_relative 'parselly/parser'
|
|
|
8
8
|
require_relative 'parselly/version'
|
|
9
9
|
|
|
10
10
|
module Parselly
|
|
11
|
+
ParseResult = Struct.new(:ast, :errors)
|
|
12
|
+
|
|
13
|
+
class ParseError < StandardError
|
|
14
|
+
attr_reader :error
|
|
15
|
+
|
|
16
|
+
def initialize(error)
|
|
17
|
+
@error = error
|
|
18
|
+
super(error[:message])
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def parse(selector, tolerant: false)
|
|
23
|
+
Parser.new.parse(selector, tolerant: tolerant)
|
|
24
|
+
end
|
|
25
|
+
|
|
11
26
|
def sanitize(selector)
|
|
12
27
|
scanner = StringScanner.new(selector)
|
|
13
28
|
result = +''
|
|
@@ -46,5 +61,5 @@ module Parselly
|
|
|
46
61
|
"\\#{char.ord.to_s(16)} "
|
|
47
62
|
end
|
|
48
63
|
|
|
49
|
-
module_function :sanitize, :escaped_hex
|
|
64
|
+
module_function :parse, :sanitize, :escaped_hex
|
|
50
65
|
end
|
data/parser.y
CHANGED
|
@@ -82,7 +82,7 @@ rule
|
|
|
82
82
|
|
|
83
83
|
type_selector
|
|
84
84
|
: IDENT
|
|
85
|
-
{ result = Node.new(:type_selector, val[0], @current_position) }
|
|
85
|
+
{ result = Node.new(:type_selector, identifier_value(val[0]), @current_position, raw_value: identifier_raw(val[0])) }
|
|
86
86
|
| STAR
|
|
87
87
|
{ result = Node.new(:universal_selector, '*', @current_position) }
|
|
88
88
|
;
|
|
@@ -102,30 +102,30 @@ rule
|
|
|
102
102
|
|
|
103
103
|
id_selector
|
|
104
104
|
: HASH IDENT
|
|
105
|
-
{ result = Node.new(:id_selector, val[1], @current_position) }
|
|
105
|
+
{ result = Node.new(:id_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])) }
|
|
106
106
|
;
|
|
107
107
|
|
|
108
108
|
class_selector
|
|
109
109
|
: DOT IDENT
|
|
110
|
-
{ result = Node.new(:class_selector, val[1], @current_position) }
|
|
110
|
+
{ result = Node.new(:class_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])) }
|
|
111
111
|
;
|
|
112
112
|
|
|
113
113
|
attribute_selector
|
|
114
114
|
: LBRACKET IDENT RBRACKET
|
|
115
|
-
{ result = Node.new(:attribute_selector, val[1], @current_position) }
|
|
115
|
+
{ result = Node.new(:attribute_selector, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])) }
|
|
116
116
|
| LBRACKET IDENT attr_matcher STRING RBRACKET
|
|
117
117
|
{
|
|
118
118
|
result = Node.new(:attribute_selector, nil, @current_position)
|
|
119
|
-
result.add_child(Node.new(:attribute, val[1], @current_position))
|
|
119
|
+
result.add_child(Node.new(:attribute, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])))
|
|
120
120
|
result.add_child(val[2])
|
|
121
121
|
result.add_child(Node.new(:value, val[3], @current_position))
|
|
122
122
|
}
|
|
123
123
|
| LBRACKET IDENT attr_matcher IDENT RBRACKET
|
|
124
124
|
{
|
|
125
125
|
result = Node.new(:attribute_selector, nil, @current_position)
|
|
126
|
-
result.add_child(Node.new(:attribute, val[1], @current_position))
|
|
126
|
+
result.add_child(Node.new(:attribute, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])))
|
|
127
127
|
result.add_child(val[2])
|
|
128
|
-
result.add_child(Node.new(:value, val[3], @current_position))
|
|
128
|
+
result.add_child(Node.new(:value, identifier_value(val[3]), @current_position, raw_value: identifier_raw(val[3])))
|
|
129
129
|
}
|
|
130
130
|
;
|
|
131
131
|
|
|
@@ -146,18 +146,24 @@ rule
|
|
|
146
146
|
|
|
147
147
|
pseudo_class_selector
|
|
148
148
|
: COLON IDENT
|
|
149
|
-
{ result = Node.new(:pseudo_class, val[1], @current_position) }
|
|
149
|
+
{ result = Node.new(:pseudo_class, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1])) }
|
|
150
150
|
| COLON IDENT LPAREN any_value RPAREN
|
|
151
151
|
{
|
|
152
|
-
fn = Node.new(:pseudo_function, val[1], @current_position)
|
|
152
|
+
fn = Node.new(:pseudo_function, identifier_value(val[1]), @current_position, raw_value: identifier_raw(val[1]))
|
|
153
153
|
fn.add_child(val[3])
|
|
154
154
|
result = fn
|
|
155
155
|
}
|
|
156
|
+
| IDENT LPAREN any_value RPAREN
|
|
157
|
+
{
|
|
158
|
+
fn = Node.new(:pseudo_function, identifier_value(val[0]), @current_position, raw_value: identifier_raw(val[0]))
|
|
159
|
+
fn.add_child(val[2])
|
|
160
|
+
result = fn
|
|
161
|
+
}
|
|
156
162
|
;
|
|
157
163
|
|
|
158
164
|
pseudo_element_selector
|
|
159
165
|
: COLON COLON IDENT
|
|
160
|
-
{ result = Node.new(:pseudo_element, val[2], @current_position) }
|
|
166
|
+
{ result = Node.new(:pseudo_element, identifier_value(val[2]), @current_position, raw_value: identifier_raw(val[2])) }
|
|
161
167
|
;
|
|
162
168
|
|
|
163
169
|
any_value
|
|
@@ -265,21 +271,99 @@ CAN_END_COMPOUND = Set[:IDENT, :STAR, :RPAREN, :RBRACKET].freeze
|
|
|
265
271
|
CAN_START_COMPOUND = Set[:IDENT, :STAR, :DOT, :HASH, :LBRACKET, :COLON].freeze
|
|
266
272
|
TYPE_SELECTOR_TYPES = Set[:IDENT, :STAR].freeze
|
|
267
273
|
SUBCLASS_SELECTOR_TYPES = Set[:DOT, :HASH, :LBRACKET, :COLON].freeze
|
|
274
|
+
SUBCLASS_SELECTOR_END_TYPES = Set[:IDENT, :RBRACKET, :RPAREN].freeze
|
|
268
275
|
NTH_PSEUDO_NAMES = Set['nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type', 'nth-col', 'nth-last-col'].freeze
|
|
269
276
|
AN_PLUS_B_REGEX = /^(even|odd|[+-]?\d*n(?:[+-]\d+)?|[+-]?n(?:[+-]\d+)?|\d+)$/.freeze
|
|
270
277
|
|
|
271
278
|
---- inner
|
|
272
|
-
def parse(input)
|
|
279
|
+
def parse(input, tolerant: false)
|
|
280
|
+
@tolerant = tolerant
|
|
281
|
+
@errors = []
|
|
282
|
+
@error_index = nil
|
|
283
|
+
@suppress_errors = false
|
|
273
284
|
@lexer = Parselly::Lexer.new(input)
|
|
274
|
-
|
|
285
|
+
begin
|
|
286
|
+
@tokens = @lexer.tokenize
|
|
287
|
+
rescue RuntimeError => e
|
|
288
|
+
if tolerant
|
|
289
|
+
@errors << parse_error_from_exception(e)
|
|
290
|
+
return Parselly::ParseResult.new(nil, @errors)
|
|
291
|
+
end
|
|
292
|
+
raise
|
|
293
|
+
end
|
|
275
294
|
preprocess_tokens!
|
|
276
295
|
@index = 0
|
|
277
|
-
@current_position = { line: 1, column: 1 }
|
|
296
|
+
@current_position = { line: 1, column: 1, offset: 0 }
|
|
297
|
+
|
|
298
|
+
if tolerant
|
|
299
|
+
ast = parse_with_recovery
|
|
300
|
+
normalize_an_plus_b(ast) if ast
|
|
301
|
+
return Parselly::ParseResult.new(ast, @errors)
|
|
302
|
+
end
|
|
303
|
+
|
|
278
304
|
ast = do_parse
|
|
279
305
|
normalize_an_plus_b(ast)
|
|
280
306
|
ast
|
|
281
307
|
end
|
|
282
308
|
|
|
309
|
+
def parse_with_recovery
|
|
310
|
+
do_parse
|
|
311
|
+
rescue Parselly::ParseError, RuntimeError
|
|
312
|
+
parse_partial_ast
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def parse_partial_ast
|
|
316
|
+
return nil unless @tokens && !@tokens.empty?
|
|
317
|
+
|
|
318
|
+
eof_token = @tokens.last if @tokens.last && @tokens.last[0] == false
|
|
319
|
+
tokens = @tokens.dup
|
|
320
|
+
tokens.pop if eof_token
|
|
321
|
+
limit = @error_index || tokens.length
|
|
322
|
+
|
|
323
|
+
while limit > 0
|
|
324
|
+
truncated = tokens[0...limit]
|
|
325
|
+
truncated << eof_token if eof_token
|
|
326
|
+
begin
|
|
327
|
+
return parse_from_tokens(truncated, suppress_errors: true)
|
|
328
|
+
rescue Parselly::ParseError, RuntimeError
|
|
329
|
+
limit -= 1
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
nil
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
def parse_from_tokens(tokens, suppress_errors: false)
|
|
336
|
+
@tokens = tokens
|
|
337
|
+
@index = 0
|
|
338
|
+
@current_position = { line: 1, column: 1, offset: 0 }
|
|
339
|
+
@suppress_errors = suppress_errors
|
|
340
|
+
do_parse
|
|
341
|
+
ensure
|
|
342
|
+
@suppress_errors = false
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def parse_error_from_exception(error)
|
|
346
|
+
line = nil
|
|
347
|
+
column = nil
|
|
348
|
+
offset = nil
|
|
349
|
+
if error.message =~ /at (\d+):(\d+)/
|
|
350
|
+
line = Regexp.last_match(1).to_i
|
|
351
|
+
column = Regexp.last_match(2).to_i
|
|
352
|
+
end
|
|
353
|
+
if error.message =~ /offset (\d+)/
|
|
354
|
+
offset = Regexp.last_match(1).to_i
|
|
355
|
+
end
|
|
356
|
+
{ message: error.message, line: line, column: column, offset: offset }
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def identifier_value(token)
|
|
360
|
+
token.respond_to?(:value) ? token.value : token
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def identifier_raw(token)
|
|
364
|
+
token.respond_to?(:raw) ? token.raw : token
|
|
365
|
+
end
|
|
366
|
+
|
|
283
367
|
def preprocess_tokens!
|
|
284
368
|
return if @tokens.size <= 1
|
|
285
369
|
|
|
@@ -294,7 +378,7 @@ def preprocess_tokens!
|
|
|
294
378
|
if i < last_idx
|
|
295
379
|
next_token = @tokens[i + 1]
|
|
296
380
|
if needs_descendant?(token, next_token)
|
|
297
|
-
pos = { line: token[2][:line], column: token[2][:column] }
|
|
381
|
+
pos = { line: token[2][:line], column: token[2][:column], offset: token[2][:offset] }
|
|
298
382
|
new_tokens[new_tokens_idx] = [:DESCENDANT, ' ', pos]
|
|
299
383
|
new_tokens_idx += 1
|
|
300
384
|
end
|
|
@@ -314,8 +398,11 @@ def needs_descendant?(current, next_tok)
|
|
|
314
398
|
next_type = next_tok[0]
|
|
315
399
|
|
|
316
400
|
# Type selector followed by subclass selector = same compound
|
|
317
|
-
|
|
318
|
-
|
|
401
|
+
# Subclass selector followed by subclass selector = same compound
|
|
402
|
+
if SUBCLASS_SELECTOR_TYPES.include?(next_type)
|
|
403
|
+
return false if TYPE_SELECTOR_TYPES.include?(current_type) ||
|
|
404
|
+
SUBCLASS_SELECTOR_END_TYPES.include?(current_type)
|
|
405
|
+
end
|
|
319
406
|
|
|
320
407
|
CAN_END_COMPOUND.include?(current_type) && CAN_START_COMPOUND.include?(next_type)
|
|
321
408
|
end
|
|
@@ -361,5 +448,16 @@ end
|
|
|
361
448
|
def on_error(token_id, val, vstack)
|
|
362
449
|
token_name = token_to_str(token_id) || '?'
|
|
363
450
|
pos = @current_position || { line: '?', column: '?' }
|
|
364
|
-
|
|
451
|
+
error = {
|
|
452
|
+
message: "Parse error: unexpected #{token_name} '#{val}' at #{pos[:line]}:#{pos[:column]}",
|
|
453
|
+
line: pos[:line],
|
|
454
|
+
column: pos[:column],
|
|
455
|
+
offset: pos[:offset]
|
|
456
|
+
}
|
|
457
|
+
if @tolerant
|
|
458
|
+
@errors << error unless @suppress_errors
|
|
459
|
+
@error_index ||= [@index - 1, 0].max
|
|
460
|
+
raise Parselly::ParseError, error
|
|
461
|
+
end
|
|
462
|
+
raise error[:message]
|
|
365
463
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: parselly
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yudai Takada
|
|
@@ -51,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
51
51
|
- !ruby/object:Gem::Version
|
|
52
52
|
version: '0'
|
|
53
53
|
requirements: []
|
|
54
|
-
rubygems_version:
|
|
54
|
+
rubygems_version: 4.0.4
|
|
55
55
|
specification_version: 4
|
|
56
56
|
summary: Pure Ruby CSS selector parser.
|
|
57
57
|
test_files: []
|