parselly 1.0.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.
@@ -653,37 +653,131 @@ end
653
653
  end
654
654
  ###### racc/parser.rb end
655
655
 
656
+ require 'set'
657
+
658
+ # Pre-computed sets for faster lookup
659
+ CAN_END_COMPOUND = Set[:IDENT, :STAR, :RPAREN, :RBRACKET].freeze
660
+ CAN_START_COMPOUND = Set[:IDENT, :STAR, :DOT, :HASH, :LBRACKET, :COLON].freeze
661
+ TYPE_SELECTOR_TYPES = Set[:IDENT, :STAR].freeze
662
+ SUBCLASS_SELECTOR_TYPES = Set[:DOT, :HASH, :LBRACKET, :COLON].freeze
663
+ SUBCLASS_SELECTOR_END_TYPES = Set[:IDENT, :RBRACKET, :RPAREN].freeze
664
+ NTH_PSEUDO_NAMES = Set['nth-child', 'nth-last-child', 'nth-of-type', 'nth-last-of-type', 'nth-col', 'nth-last-col'].freeze
665
+ AN_PLUS_B_REGEX = /^(even|odd|[+-]?\d*n(?:[+-]\d+)?|[+-]?n(?:[+-]\d+)?|\d+)$/.freeze
656
666
 
657
667
  module Parselly
658
668
  class Parser < Racc::Parser
659
669
 
660
- module_eval(<<'...end parser.y/module_eval...', 'parser.y', 263)
661
- 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
662
676
  @lexer = Parselly::Lexer.new(input)
663
- @tokens = @lexer.tokenize
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
664
686
  preprocess_tokens!
665
687
  @index = 0
666
- @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
+
667
696
  ast = do_parse
668
697
  normalize_an_plus_b(ast)
669
698
  ast
670
699
  end
671
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
+
672
759
  def preprocess_tokens!
673
- new_tokens = []
674
- i = 0
675
- while i < @tokens.size
676
- token = @tokens[i]
677
- next_token = @tokens[i + 1]
678
- new_tokens << token
679
- if next_token && needs_descendant?(token, next_token)
680
- pos = { line: token[2][:line], column: token[2][:column] }
681
- new_tokens << [:DESCENDANT, ' ', pos]
760
+ return if @tokens.size <= 1
761
+
762
+ new_tokens = Array.new(@tokens.size + (@tokens.size / 2)) # Pre-allocate with conservative estimate
763
+ new_tokens_idx = 0
764
+
765
+ last_idx = @tokens.size - 1
766
+ @tokens.each_with_index do |token, i|
767
+ new_tokens[new_tokens_idx] = token
768
+ new_tokens_idx += 1
769
+
770
+ if i < last_idx
771
+ next_token = @tokens[i + 1]
772
+ if needs_descendant?(token, next_token)
773
+ pos = { line: token[2][:line], column: token[2][:column], offset: token[2][:offset] }
774
+ new_tokens[new_tokens_idx] = [:DESCENDANT, ' ', pos]
775
+ new_tokens_idx += 1
776
+ end
682
777
  end
683
- i += 1
684
778
  end
685
779
 
686
- @tokens = new_tokens
780
+ @tokens = new_tokens.first(new_tokens_idx)
687
781
  end
688
782
 
689
783
  # Insert DESCENDANT combinator if:
@@ -695,62 +789,42 @@ def needs_descendant?(current, next_tok)
695
789
  current_type = current[0]
696
790
  next_type = next_tok[0]
697
791
 
698
- can_end = can_end_compound?(current_type)
699
- can_start = can_start_compound?(next_type)
700
-
701
792
  # Type selector followed by subclass selector = same compound
702
- if [:IDENT, :STAR].include?(current_type) &&
703
- [:DOT, :HASH, :LBRACKET, :COLON].include?(next_type)
704
- return false
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)
705
797
  end
706
798
 
707
- can_end && can_start
708
- end
709
-
710
- def can_end_compound?(token_type)
711
- [:IDENT, :STAR, :RPAREN, :RBRACKET].include?(token_type)
712
- end
713
-
714
- def can_start_compound?(token_type)
715
- # Type selectors and subclass selectors can start a compound selector
716
- [:IDENT, :STAR, :DOT, :HASH, :LBRACKET, :COLON].include?(token_type)
799
+ CAN_END_COMPOUND.include?(current_type) && CAN_START_COMPOUND.include?(next_type)
717
800
  end
718
801
 
719
802
  def normalize_an_plus_b(node)
720
803
  return unless node.respond_to?(:children) && node.children
721
804
 
722
- if node.type == :pseudo_function && nth_pseudo?(node.value)
805
+ if node.type == :pseudo_function && NTH_PSEUDO_NAMES.include?(node.value)
723
806
  child = node.children.first
724
- if child && child.type == :selector_list
807
+ if child&.type == :selector_list
725
808
  an_plus_b_value = extract_an_plus_b_value(child)
726
809
  if an_plus_b_value
727
- node.children[0] = Node.new(:an_plus_b, an_plus_b_value, child.position)
810
+ node.replace_child(0, Node.new(:an_plus_b, an_plus_b_value, child.position))
728
811
  end
729
812
  end
730
813
  end
731
814
  node.children.compact.each { |child| normalize_an_plus_b(child) }
732
815
  end
733
816
 
734
- def nth_pseudo?(name)
735
- %w[nth-child nth-last-child nth-of-type nth-last-of-type nth-col nth-last-col].include?(name)
736
- end
737
-
738
817
  def extract_an_plus_b_value(selector_list_node)
739
818
  return nil unless selector_list_node.children.size == 1
740
819
 
741
820
  seq = selector_list_node.children.first
742
- return nil unless seq.type == :simple_selector_sequence
743
- return nil unless seq.children.size == 1
821
+ return nil unless seq.type == :simple_selector_sequence && seq.children.size == 1
744
822
 
745
823
  type_sel = seq.children.first
746
824
  return nil unless type_sel.type == :type_selector
747
825
 
748
826
  value = type_sel.value
749
- if value =~ /^(even|odd|[+-]?\d*n(?:[+-]\d+)?|[+-]?n(?:[+-]\d+)?|\d+)$/
750
- value
751
- else
752
- nil
753
- end
827
+ value if value =~ AN_PLUS_B_REGEX
754
828
  end
755
829
 
756
830
  def next_token
@@ -766,88 +840,101 @@ end
766
840
  def on_error(token_id, val, vstack)
767
841
  token_name = token_to_str(token_id) || '?'
768
842
  pos = @current_position || { line: '?', column: '?' }
769
- raise "Parse error: unexpected #{token_name} '#{val}' at #{pos[:line]}:#{pos[:column]}"
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]
770
855
  end
771
856
  ...end parser.y/module_eval...
772
857
  ##### State transition tables begin ###
773
858
 
774
859
  racc_action_table = [
775
- 55, 57, 60, 14, 15, 8, 16, 35, 18, 67,
776
- 17, 19, 25, 26, 27, 28, 32, 36, 68, 79,
777
- 7, 33, 61, 14, 15, 8, 16, 43, 80, 72,
778
- 17, 71, 25, 26, 27, 28, 45, 46, 47, 48,
779
- 49, 50, 7, 54, 53, 14, 15, 8, 16, 34,
780
- 7, 37, 17, 14, 15, 8, 16, 19, 7, 51,
781
- 17, 14, 15, 8, 16, 82, 7, 52, 17, 14,
782
- 15, 8, 16, 88, 83, 65, 17, 14, 15, 66,
783
- 16, 69, 89, 70, 17, 14, 15, 73, 16, 77,
784
- 78, 81, 17, 25, 26, 27, 28, 25, 26, 27,
785
- 28, 73, 86, 87, 90, 91, 92, 93 ]
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 ]
786
873
 
787
874
  racc_action_check = [
788
- 51, 51, 51, 51, 51, 51, 51, 17, 1, 55,
789
- 51, 2, 51, 51, 51, 51, 14, 17, 55, 70,
790
- 73, 15, 51, 73, 73, 73, 73, 34, 70, 61,
791
- 73, 61, 73, 73, 73, 73, 34, 34, 34, 34,
792
- 34, 34, 0, 44, 44, 0, 0, 0, 0, 16,
793
- 19, 18, 0, 19, 19, 19, 19, 20, 22, 35,
794
- 19, 22, 22, 22, 22, 72, 64, 36, 22, 64,
795
- 64, 64, 64, 81, 72, 53, 64, 4, 4, 54,
796
- 4, 56, 81, 60, 4, 30, 30, 62, 30, 67,
797
- 68, 71, 30, 3, 3, 3, 3, 23, 23, 23,
798
- 23, 74, 79, 80, 82, 83, 88, 89 ]
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 ]
799
888
 
800
889
  racc_action_pointer = [
801
- 40, 8, -2, 79, 72, nil, nil, nil, nil, nil,
802
- nil, nil, nil, nil, 14, 19, 47, 5, 51, 48,
803
- 44, nil, 56, 83, nil, nil, nil, nil, nil, nil,
804
- 80, nil, nil, nil, 18, 49, 65, nil, nil, nil,
805
- nil, nil, nil, nil, 41, nil, nil, nil, nil, nil,
806
- nil, -2, nil, 66, 70, -6, 70, nil, nil, nil,
807
- 81, 27, 74, nil, 64, nil, nil, 85, 86, nil,
808
- 4, 89, 50, 18, 88, nil, nil, nil, nil, 98,
809
- 99, 58, 100, 101, nil, nil, nil, nil, 102, 103,
810
- 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 ]
811
900
 
812
901
  racc_action_default = [
813
- -61, -61, -2, -6, -16, -14, -15, -19, -20, -21,
814
- -22, -23, -24, -25, -61, -61, -61, -61, -61, -61,
815
- -2, -4, -61, -6, -8, -9, -10, -11, -12, -13,
816
- -16, -18, -26, -27, -61, -37, -61, 94, -1, -3,
817
- -5, -7, -17, -28, -61, -31, -32, -33, -34, -35,
818
- -36, -61, -39, -61, -61, -19, -61, -40, -41, -42,
819
- -54, -61, -56, -59, -61, -29, -30, -61, -61, -38,
820
- -45, -61, -53, -61, -56, -58, -60, -46, -47, -61,
821
- -61, -50, -61, -61, -55, -57, -43, -44, -61, -61,
822
- -51, -52, -48, -49 ]
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 ]
823
912
 
824
913
  racc_goto_table = [
825
- 2, 30, 31, 64, 62, 21, 75, 1, 40, 24,
826
- 29, 44, 56, 58, 59, nil, nil, nil, 85, 38,
827
- nil, nil, nil, 39, nil, 64, 84, 30, 42, 41,
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,
828
918
  nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
829
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
830
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
831
- nil, nil, nil, nil, 76 ]
919
+ nil, nil, nil, nil, 74 ]
832
920
 
833
921
  racc_goto_check = [
834
- 2, 12, 13, 6, 23, 4, 25, 1, 5, 8,
835
- 10, 19, 20, 21, 22, nil, nil, nil, 25, 2,
836
- nil, nil, nil, 4, nil, 6, 23, 12, 13, 8,
837
- nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
838
- 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,
839
926
  nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
840
927
  nil, nil, nil, nil, 2 ]
841
928
 
842
929
  racc_goto_pointer = [
843
- nil, 7, 0, nil, 3, -14, -48, nil, 6, nil,
844
- 6, nil, -3, -2, nil, nil, nil, nil, nil, -23,
845
- -39, -38, -37, -47, nil, -56 ]
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 ]
846
933
 
847
934
  racc_goto_default = [
848
- nil, nil, 63, 20, nil, 3, 22, 23, nil, 4,
935
+ nil, nil, 53, 20, nil, 3, 54, 23, nil, 4,
849
936
  nil, 5, 6, nil, 9, 10, 11, 12, 13, nil,
850
- nil, nil, nil, nil, 74, nil ]
937
+ nil, 48, 49, 52, 72, nil ]
851
938
 
852
939
  racc_reduce_table = [
853
940
  0, 0, :racc_error,
@@ -889,32 +976,33 @@ racc_reduce_table = [
889
976
  1, 45, :_reduce_36,
890
977
  2, 43, :_reduce_37,
891
978
  5, 43, :_reduce_38,
892
- 3, 44, :_reduce_39,
893
- 1, 46, :_reduce_40,
979
+ 4, 43, :_reduce_39,
980
+ 3, 44, :_reduce_40,
894
981
  1, 46, :_reduce_41,
895
982
  1, 46, :_reduce_42,
896
- 4, 47, :_reduce_43,
983
+ 1, 46, :_reduce_43,
897
984
  4, 47, :_reduce_44,
898
- 2, 47, :_reduce_45,
899
- 3, 47, :_reduce_46,
985
+ 4, 47, :_reduce_45,
986
+ 2, 47, :_reduce_46,
900
987
  3, 47, :_reduce_47,
901
- 5, 47, :_reduce_48,
988
+ 3, 47, :_reduce_48,
902
989
  5, 47, :_reduce_49,
903
- 3, 47, :_reduce_50,
904
- 4, 47, :_reduce_51,
990
+ 5, 47, :_reduce_50,
991
+ 3, 47, :_reduce_51,
905
992
  4, 47, :_reduce_52,
906
- 2, 47, :_reduce_53,
907
- 1, 47, :_reduce_54,
908
- 2, 50, :_reduce_55,
909
- 0, 51, :_reduce_56,
910
- 2, 51, :_reduce_57,
911
- 2, 48, :_reduce_58,
912
- 1, 49, :_reduce_59,
913
- 2, 49, :_reduce_60 ]
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 ]
914
1002
 
915
- racc_reduce_n = 61
1003
+ racc_reduce_n = 62
916
1004
 
917
- racc_shift_n = 94
1005
+ racc_shift_n = 98
918
1006
 
919
1007
  racc_token_table = {
920
1008
  false => 0,
@@ -1172,7 +1260,7 @@ module_eval(<<'.,.,', 'parser.y', 79)
1172
1260
 
1173
1261
  module_eval(<<'.,.,', 'parser.y', 84)
1174
1262
  def _reduce_19(val, _values, result)
1175
- 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]))
1176
1264
  result
1177
1265
  end
1178
1266
  .,.,
@@ -1221,21 +1309,21 @@ module_eval(<<'.,.,', 'parser.y', 99)
1221
1309
 
1222
1310
  module_eval(<<'.,.,', 'parser.y', 104)
1223
1311
  def _reduce_26(val, _values, result)
1224
- 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]))
1225
1313
  result
1226
1314
  end
1227
1315
  .,.,
1228
1316
 
1229
1317
  module_eval(<<'.,.,', 'parser.y', 109)
1230
1318
  def _reduce_27(val, _values, result)
1231
- 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]))
1232
1320
  result
1233
1321
  end
1234
1322
  .,.,
1235
1323
 
1236
1324
  module_eval(<<'.,.,', 'parser.y', 114)
1237
1325
  def _reduce_28(val, _values, result)
1238
- 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]))
1239
1327
  result
1240
1328
  end
1241
1329
  .,.,
@@ -1243,7 +1331,7 @@ module_eval(<<'.,.,', 'parser.y', 114)
1243
1331
  module_eval(<<'.,.,', 'parser.y', 117)
1244
1332
  def _reduce_29(val, _values, result)
1245
1333
  result = Node.new(:attribute_selector, nil, @current_position)
1246
- 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])))
1247
1335
  result.add_child(val[2])
1248
1336
  result.add_child(Node.new(:value, val[3], @current_position))
1249
1337
 
@@ -1254,9 +1342,9 @@ module_eval(<<'.,.,', 'parser.y', 117)
1254
1342
  module_eval(<<'.,.,', 'parser.y', 124)
1255
1343
  def _reduce_30(val, _values, result)
1256
1344
  result = Node.new(:attribute_selector, nil, @current_position)
1257
- 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])))
1258
1346
  result.add_child(val[2])
1259
- 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])))
1260
1348
 
1261
1349
  result
1262
1350
  end
@@ -1306,14 +1394,14 @@ module_eval(<<'.,.,', 'parser.y', 143)
1306
1394
 
1307
1395
  module_eval(<<'.,.,', 'parser.y', 148)
1308
1396
  def _reduce_37(val, _values, result)
1309
- 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]))
1310
1398
  result
1311
1399
  end
1312
1400
  .,.,
1313
1401
 
1314
1402
  module_eval(<<'.,.,', 'parser.y', 151)
1315
1403
  def _reduce_38(val, _values, result)
1316
- 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]))
1317
1405
  fn.add_child(val[3])
1318
1406
  result = fn
1319
1407
 
@@ -1321,36 +1409,46 @@ module_eval(<<'.,.,', 'parser.y', 151)
1321
1409
  end
1322
1410
  .,.,
1323
1411
 
1324
- module_eval(<<'.,.,', 'parser.y', 159)
1412
+ module_eval(<<'.,.,', 'parser.y', 157)
1325
1413
  def _reduce_39(val, _values, result)
1326
- result = Node.new(:pseudo_element, val[2], @current_position)
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
+
1327
1418
  result
1328
1419
  end
1329
1420
  .,.,
1330
1421
 
1331
- module_eval(<<'.,.,', 'parser.y', 164)
1422
+ module_eval(<<'.,.,', 'parser.y', 165)
1332
1423
  def _reduce_40(val, _values, result)
1333
- result = Node.new(:argument, val[0], @current_position)
1424
+ result = Node.new(:pseudo_element, identifier_value(val[2]), @current_position, raw_value: identifier_raw(val[2]))
1334
1425
  result
1335
1426
  end
1336
1427
  .,.,
1337
1428
 
1338
- module_eval(<<'.,.,', 'parser.y', 166)
1429
+ module_eval(<<'.,.,', 'parser.y', 170)
1339
1430
  def _reduce_41(val, _values, result)
1340
- result = val[0]
1431
+ result = Node.new(:argument, val[0], @current_position)
1341
1432
  result
1342
1433
  end
1343
1434
  .,.,
1344
1435
 
1345
- module_eval(<<'.,.,', 'parser.y', 168)
1436
+ module_eval(<<'.,.,', 'parser.y', 172)
1346
1437
  def _reduce_42(val, _values, result)
1347
1438
  result = val[0]
1348
1439
  result
1349
1440
  end
1350
1441
  .,.,
1351
1442
 
1352
- module_eval(<<'.,.,', 'parser.y', 175)
1443
+ module_eval(<<'.,.,', 'parser.y', 174)
1353
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)
1354
1452
  # Handle 'An+B' like '2n+1'
1355
1453
  result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}+#{val[3]}", @current_position)
1356
1454
 
@@ -1358,8 +1456,8 @@ module_eval(<<'.,.,', 'parser.y', 175)
1358
1456
  end
1359
1457
  .,.,
1360
1458
 
1361
- module_eval(<<'.,.,', 'parser.y', 180)
1362
- def _reduce_44(val, _values, result)
1459
+ module_eval(<<'.,.,', 'parser.y', 186)
1460
+ def _reduce_45(val, _values, result)
1363
1461
  # Handle 'An-B' like '2n-1'
1364
1462
  result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}-#{val[3]}", @current_position)
1365
1463
 
@@ -1367,8 +1465,8 @@ module_eval(<<'.,.,', 'parser.y', 180)
1367
1465
  end
1368
1466
  .,.,
1369
1467
 
1370
- module_eval(<<'.,.,', 'parser.y', 185)
1371
- def _reduce_45(val, _values, result)
1468
+ module_eval(<<'.,.,', 'parser.y', 191)
1469
+ def _reduce_46(val, _values, result)
1372
1470
  # Handle 'An' like '2n' or composite like '2n-1' (when '-1' is part of IDENT)
1373
1471
  result = Node.new(:an_plus_b, "#{val[0]}#{val[1]}", @current_position)
1374
1472
 
@@ -1376,8 +1474,8 @@ module_eval(<<'.,.,', 'parser.y', 185)
1376
1474
  end
1377
1475
  .,.,
1378
1476
 
1379
- module_eval(<<'.,.,', 'parser.y', 190)
1380
- def _reduce_46(val, _values, result)
1477
+ module_eval(<<'.,.,', 'parser.y', 196)
1478
+ def _reduce_47(val, _values, result)
1381
1479
  # Handle 'n+B' like 'n+5' or keywords followed by offset (rare but valid)
1382
1480
  result = Node.new(:an_plus_b, "#{val[0]}+#{val[2]}", @current_position)
1383
1481
 
@@ -1385,8 +1483,8 @@ module_eval(<<'.,.,', 'parser.y', 190)
1385
1483
  end
1386
1484
  .,.,
1387
1485
 
1388
- module_eval(<<'.,.,', 'parser.y', 195)
1389
- def _reduce_47(val, _values, result)
1486
+ module_eval(<<'.,.,', 'parser.y', 201)
1487
+ def _reduce_48(val, _values, result)
1390
1488
  # Handle 'n-B' like 'n-3'
1391
1489
  result = Node.new(:an_plus_b, "#{val[0]}-#{val[2]}", @current_position)
1392
1490
 
@@ -1394,8 +1492,8 @@ module_eval(<<'.,.,', 'parser.y', 195)
1394
1492
  end
1395
1493
  .,.,
1396
1494
 
1397
- module_eval(<<'.,.,', 'parser.y', 201)
1398
- def _reduce_48(val, _values, result)
1495
+ module_eval(<<'.,.,', 'parser.y', 207)
1496
+ def _reduce_49(val, _values, result)
1399
1497
  # Handle '-An+B' like '-2n+1'
1400
1498
  result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}+#{val[4]}", @current_position)
1401
1499
 
@@ -1403,8 +1501,8 @@ module_eval(<<'.,.,', 'parser.y', 201)
1403
1501
  end
1404
1502
  .,.,
1405
1503
 
1406
- module_eval(<<'.,.,', 'parser.y', 206)
1407
- def _reduce_49(val, _values, result)
1504
+ module_eval(<<'.,.,', 'parser.y', 212)
1505
+ def _reduce_50(val, _values, result)
1408
1506
  # Handle '-An-B' like '-2n-1'
1409
1507
  result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}-#{val[4]}", @current_position)
1410
1508
 
@@ -1412,8 +1510,8 @@ module_eval(<<'.,.,', 'parser.y', 206)
1412
1510
  end
1413
1511
  .,.,
1414
1512
 
1415
- module_eval(<<'.,.,', 'parser.y', 211)
1416
- def _reduce_50(val, _values, result)
1513
+ module_eval(<<'.,.,', 'parser.y', 217)
1514
+ def _reduce_51(val, _values, result)
1417
1515
  # Handle '-An' like '-2n' or composite like '-2n+1' (when '+1' is part of IDENT)
1418
1516
  result = Node.new(:an_plus_b, "-#{val[1]}#{val[2]}", @current_position)
1419
1517
 
@@ -1421,8 +1519,8 @@ module_eval(<<'.,.,', 'parser.y', 211)
1421
1519
  end
1422
1520
  .,.,
1423
1521
 
1424
- module_eval(<<'.,.,', 'parser.y', 216)
1425
- def _reduce_51(val, _values, result)
1522
+ module_eval(<<'.,.,', 'parser.y', 222)
1523
+ def _reduce_52(val, _values, result)
1426
1524
  # Handle '-n+B' like '-n+3'
1427
1525
  result = Node.new(:an_plus_b, "-#{val[1]}+#{val[3]}", @current_position)
1428
1526
 
@@ -1430,8 +1528,8 @@ module_eval(<<'.,.,', 'parser.y', 216)
1430
1528
  end
1431
1529
  .,.,
1432
1530
 
1433
- module_eval(<<'.,.,', 'parser.y', 221)
1434
- def _reduce_52(val, _values, result)
1531
+ module_eval(<<'.,.,', 'parser.y', 227)
1532
+ def _reduce_53(val, _values, result)
1435
1533
  # Handle '-n-B' like '-n-2'
1436
1534
  result = Node.new(:an_plus_b, "-#{val[1]}-#{val[3]}", @current_position)
1437
1535
 
@@ -1439,8 +1537,8 @@ module_eval(<<'.,.,', 'parser.y', 221)
1439
1537
  end
1440
1538
  .,.,
1441
1539
 
1442
- module_eval(<<'.,.,', 'parser.y', 226)
1443
- def _reduce_53(val, _values, result)
1540
+ module_eval(<<'.,.,', 'parser.y', 232)
1541
+ def _reduce_54(val, _values, result)
1444
1542
  # Handle '-n' or composite like '-n+3' (when '+3' is part of IDENT)
1445
1543
  result = Node.new(:an_plus_b, "-#{val[1]}", @current_position)
1446
1544
 
@@ -1448,8 +1546,8 @@ module_eval(<<'.,.,', 'parser.y', 226)
1448
1546
  end
1449
1547
  .,.,
1450
1548
 
1451
- module_eval(<<'.,.,', 'parser.y', 232)
1452
- def _reduce_54(val, _values, result)
1549
+ module_eval(<<'.,.,', 'parser.y', 238)
1550
+ def _reduce_55(val, _values, result)
1453
1551
  # Handle just a number like '3'
1454
1552
  result = Node.new(:an_plus_b, val[0].to_s, @current_position)
1455
1553
 
@@ -1457,29 +1555,29 @@ module_eval(<<'.,.,', 'parser.y', 232)
1457
1555
  end
1458
1556
  .,.,
1459
1557
 
1460
- module_eval(<<'.,.,', 'parser.y', 245)
1461
- def _reduce_55(val, _values, result)
1558
+ module_eval(<<'.,.,', 'parser.y', 251)
1559
+ def _reduce_56(val, _values, result)
1462
1560
  result = val
1463
1561
  result
1464
1562
  end
1465
1563
  .,.,
1466
1564
 
1467
- module_eval(<<'.,.,', 'parser.y', 245)
1468
- def _reduce_56(val, _values, result)
1565
+ module_eval(<<'.,.,', 'parser.y', 251)
1566
+ def _reduce_57(val, _values, result)
1469
1567
  result = val[1] ? val[1].unshift(val[0]) : val
1470
1568
  result
1471
1569
  end
1472
1570
  .,.,
1473
1571
 
1474
- module_eval(<<'.,.,', 'parser.y', 245)
1475
- def _reduce_57(val, _values, result)
1572
+ module_eval(<<'.,.,', 'parser.y', 251)
1573
+ def _reduce_58(val, _values, result)
1476
1574
  result = val[1] ? val[1].unshift(val[0]) : val
1477
1575
  result
1478
1576
  end
1479
1577
  .,.,
1480
1578
 
1481
- module_eval(<<'.,.,', 'parser.y', 240)
1482
- def _reduce_58(val, _values, result)
1579
+ module_eval(<<'.,.,', 'parser.y', 246)
1580
+ def _reduce_59(val, _values, result)
1483
1581
  result = Node.new(:selector_list, nil, @current_position)
1484
1582
  result.add_child(val[0])
1485
1583
  val[1].each { |pair| result.add_child(pair[1]) }
@@ -1488,15 +1586,15 @@ module_eval(<<'.,.,', 'parser.y', 240)
1488
1586
  end
1489
1587
  .,.,
1490
1588
 
1491
- module_eval(<<'.,.,', 'parser.y', 248)
1492
- def _reduce_59(val, _values, result)
1589
+ module_eval(<<'.,.,', 'parser.y', 254)
1590
+ def _reduce_60(val, _values, result)
1493
1591
  result = val[0]
1494
1592
  result
1495
1593
  end
1496
1594
  .,.,
1497
1595
 
1498
- module_eval(<<'.,.,', 'parser.y', 251)
1499
- def _reduce_60(val, _values, result)
1596
+ module_eval(<<'.,.,', 'parser.y', 257)
1597
+ def _reduce_61(val, _values, result)
1500
1598
  result = Node.new(:selector, nil, val[0].position)
1501
1599
  result.add_child(val[0])
1502
1600
  result.add_child(val[1])