parselly 1.2.0 → 1.3.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/README.md +56 -9
- data/Rakefile +10 -0
- data/lib/parselly/lexer.rb +278 -68
- data/lib/parselly/node.rb +434 -205
- data/lib/parselly/parser.rb +799 -325
- data/lib/parselly/version.rb +1 -1
- data/lib/parselly.rb +57 -10
- data/parser.y +454 -101
- metadata +3 -3
data/lib/parselly/version.rb
CHANGED
data/lib/parselly.rb
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'strscan'
|
|
4
|
-
|
|
5
|
-
require_relative 'parselly/lexer'
|
|
6
|
-
require_relative 'parselly/node'
|
|
7
|
-
require_relative 'parselly/parser'
|
|
8
|
-
require_relative 'parselly/version'
|
|
4
|
+
require 'set'
|
|
9
5
|
|
|
10
6
|
module Parselly
|
|
11
|
-
ParseResult
|
|
7
|
+
class ParseResult
|
|
8
|
+
attr_accessor :ast, :errors
|
|
9
|
+
|
|
10
|
+
def initialize(ast = nil, errors = nil, **keywords)
|
|
11
|
+
@ast = keywords.fetch(:ast, ast)
|
|
12
|
+
@errors = keywords.fetch(:errors, errors || [])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def success?
|
|
16
|
+
errors.empty? && !ast.nil?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def failure?
|
|
20
|
+
!success?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def empty?
|
|
24
|
+
ast.nil?
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def first_error
|
|
28
|
+
errors.first
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_a
|
|
32
|
+
[ast, errors]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def deconstruct
|
|
36
|
+
to_a
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def deconstruct_keys(keys)
|
|
40
|
+
hash = { ast: ast, errors: errors }
|
|
41
|
+
return hash if keys.nil?
|
|
42
|
+
|
|
43
|
+
keys.each_with_object({}) { |key, result| result[key] = hash[key] if hash.key?(key) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
12
46
|
|
|
13
47
|
class ParseError < StandardError
|
|
14
48
|
attr_reader :error
|
|
@@ -19,8 +53,18 @@ module Parselly
|
|
|
19
53
|
end
|
|
20
54
|
end
|
|
21
55
|
|
|
22
|
-
|
|
23
|
-
|
|
56
|
+
class LexError < ParseError; end
|
|
57
|
+
class SyntaxError < ParseError; end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
require_relative 'parselly/lexer'
|
|
61
|
+
require_relative 'parselly/node'
|
|
62
|
+
require_relative 'parselly/parser'
|
|
63
|
+
require_relative 'parselly/version'
|
|
64
|
+
|
|
65
|
+
module Parselly
|
|
66
|
+
def parse(selector, **options)
|
|
67
|
+
Parser.new.parse(selector, **options)
|
|
24
68
|
end
|
|
25
69
|
|
|
26
70
|
def sanitize(selector)
|
|
@@ -42,12 +86,15 @@ module Parselly
|
|
|
42
86
|
elsif scanner.pos.zero? && scanner.scan(/\d/)
|
|
43
87
|
result << escaped_hex(scanner.matched)
|
|
44
88
|
# Second character is a digit and first is `-`
|
|
45
|
-
elsif scanner.pos == 1 && scanner.
|
|
46
|
-
scanner.
|
|
89
|
+
elsif scanner.pos == 1 && scanner.peek(1).match?(/\d/) &&
|
|
90
|
+
scanner.string.start_with?('-') && scanner.scan(/\d/)
|
|
47
91
|
result << escaped_hex(scanner.matched)
|
|
48
92
|
# Alphanumeric characters, `-`, `_`
|
|
49
93
|
elsif scanner.scan(/[a-zA-Z0-9\-_]/)
|
|
50
94
|
result << scanner.matched
|
|
95
|
+
# Non-ASCII characters are valid CSS identifier characters.
|
|
96
|
+
elsif scanner.scan(/[^\x00-\x7F]/)
|
|
97
|
+
result << scanner.matched
|
|
51
98
|
# Any other characters, escape them
|
|
52
99
|
elsif scanner.scan(/./)
|
|
53
100
|
result << "\\#{scanner.matched}"
|