parslet-css 0.0.1
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.
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.markdown +18 -0
- data/data/character-sets +1913 -0
- data/data/iana_character_sets.txt +829 -0
- data/lib/parslet-css.rb +19 -0
- data/lib/parslet-css/parser.rb +153 -0
- data/test/parser_test.rb +176 -0
- metadata +76 -0
data/lib/parslet-css.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'parslet'
|
|
3
|
+
|
|
4
|
+
class ParsletCSS
|
|
5
|
+
VERSION = "0.0.1"
|
|
6
|
+
|
|
7
|
+
autoload :Parser, 'parslet-css/parser'
|
|
8
|
+
|
|
9
|
+
def self.compile(str)
|
|
10
|
+
parser = ParsletCSS::Parser.new
|
|
11
|
+
parser.parse(str)
|
|
12
|
+
rescue Parslet::ParseFailed => error
|
|
13
|
+
puts parser.root.error_tree
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if __FILE__ == $0
|
|
18
|
+
ParsletCSS.compile(ARGF.read)
|
|
19
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
class ParsletCSS::Parser < Parslet::Parser
|
|
3
|
+
rule(:selectors) {
|
|
4
|
+
ignore >> selector_chars >>
|
|
5
|
+
((space? >> comma >> space? | space) >> selector_chars).repeat
|
|
6
|
+
}
|
|
7
|
+
rule(:selector_chars) {
|
|
8
|
+
match('[a-zA-Z0-9\-\._~\/*"$^:=#>+|\[\]]').repeat(1) >>
|
|
9
|
+
(str('(') >> match['a-zA-Z0-9\-+:*\[\]'].repeat(1) >> str(')')).maybe
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
rule(:lcurly) { ignore >> str('{') >> ignore }
|
|
13
|
+
rule(:declarations) { (ignore >> declaration >> semicolon? >> ignore).repeat }
|
|
14
|
+
rule(:declaration) {
|
|
15
|
+
name >> ignore >> str(':') >> ignore >> property_values
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
rule(:property_values) {
|
|
19
|
+
property_value >> (semicolon.absent? >> space >> property_value).repeat(0, 5)
|
|
20
|
+
}
|
|
21
|
+
# TODO: be more precise for margin, padding... property values
|
|
22
|
+
rule(:property_value) {
|
|
23
|
+
font_height | property_value_keywords | uri | percent | color | size |
|
|
24
|
+
float | font_family_list
|
|
25
|
+
}
|
|
26
|
+
rule(:property_value_keywords) {
|
|
27
|
+
str('no-repeat') | str('scroll') | str('inherit') |
|
|
28
|
+
str('baseline') | str('block') | str('both') | str('bold') |
|
|
29
|
+
str('inline-block') | str('!important') | str('inline')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# URL
|
|
33
|
+
# http://labs.apache.org/webarch/uri/rev-2002/rfc2396bis.html#collected-abnf
|
|
34
|
+
rule(:uri) { str('url(') >> quote? >> url.as(:url) >> quote? >> str(')') }
|
|
35
|
+
rule(:url) { protocol? >> domain? >> path }
|
|
36
|
+
rule(:protocol) {
|
|
37
|
+
(str('https') | str('http') | str('ftp') | str('gopher') | str('mailto') |
|
|
38
|
+
str('news') | str('nntp') | str('telnet') | str('wais') | str('file') |
|
|
39
|
+
str('prospero')) >> str('://')
|
|
40
|
+
}
|
|
41
|
+
rule(:protocol?) { protocol.maybe }
|
|
42
|
+
rule(:domain) { match('[a-zA-Z0-9\-\._\/]').repeat >> (str(':') >> integer).maybe }
|
|
43
|
+
rule(:domain?) { domain.maybe }
|
|
44
|
+
rule(:path) { path_chars >> query? }
|
|
45
|
+
rule(:path_chars) { (str('/').maybe >> match('[a-zA-Z0-9\-\._~]')).repeat }
|
|
46
|
+
rule(:query) {
|
|
47
|
+
str('?') >> (query_key_value >> (query_sep >> query_key_value).repeat).maybe
|
|
48
|
+
}
|
|
49
|
+
rule(:query_sep) { str('&') | semicolon }
|
|
50
|
+
rule(:query?) { query.maybe }
|
|
51
|
+
rule(:query_key_value) { name >> str('=') >> name }
|
|
52
|
+
|
|
53
|
+
rule(:percent) { sign? >> integer >> str('%') }
|
|
54
|
+
rule(:size) { sign? >> (float | integer) >> length_unit }
|
|
55
|
+
rule(:length_unit) {
|
|
56
|
+
str('em') | str('ex') | str('px') | str('in') |
|
|
57
|
+
str('cm') | str('mm') | str('pt') | str('pc')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-family-prop
|
|
61
|
+
rule(:font_family_list) {
|
|
62
|
+
font_family >> ((comma >> space? >> font_family).repeat).maybe
|
|
63
|
+
}
|
|
64
|
+
# Font family names containing whitespace [link to syntax module] should be quoted.
|
|
65
|
+
rule(:font_family) {
|
|
66
|
+
quoted | name
|
|
67
|
+
}
|
|
68
|
+
rule(:font_height) {
|
|
69
|
+
size >> str('/') >> size
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# COLORS
|
|
73
|
+
rule(:color) { color_keywords | hex_value | rgb }
|
|
74
|
+
rule(:color_keywords) {
|
|
75
|
+
str('aqua') | str('black') | str('blue') | str('fuchsia') | str('gray') |
|
|
76
|
+
str('green') | str('lime') | str('maroon') | str('navy') | str('olive') |
|
|
77
|
+
str('purple') | str('red') | str('silver') | str('teal') | str('white') |
|
|
78
|
+
str('yellow') | str('transparent')
|
|
79
|
+
}
|
|
80
|
+
rule(:hex_value) {
|
|
81
|
+
str('#') >> (digit_hex.repeat(6,6) | digit_hex.repeat(3,3))
|
|
82
|
+
}
|
|
83
|
+
rule(:digit_hex) { match('[0-9a-fA-F]') }
|
|
84
|
+
rule(:rgb) {
|
|
85
|
+
(str('rgb(') | str('rgba(') | str('hsl(')) >>
|
|
86
|
+
space? >> rgb_value >> space? >> comma >>
|
|
87
|
+
space? >> rgb_value >> space? >> comma >>
|
|
88
|
+
(space? >> rgb_value >> space? >> comma).maybe >>
|
|
89
|
+
space? >> rgb_value >> space? >> str(')')
|
|
90
|
+
}
|
|
91
|
+
rule(:rgb_value) { percent | float | integer }
|
|
92
|
+
|
|
93
|
+
# TODO: http://www.w3.org/TR/css3-2d-transforms/
|
|
94
|
+
|
|
95
|
+
rule(:rcurly) { ignore >> str('}') >> ignore }
|
|
96
|
+
|
|
97
|
+
rule(:space) { match('\s').repeat(1) }
|
|
98
|
+
rule(:space?) { space.maybe }
|
|
99
|
+
rule(:quote) { str('"') | str("'") }
|
|
100
|
+
rule(:quote?) { quote.maybe }
|
|
101
|
+
rule(:name) { match['_a-zA-Z0-9-'].repeat(1) }
|
|
102
|
+
rule(:quoted) { quote >> (quote.absent? >> any).repeat >> quote }
|
|
103
|
+
rule(:integer) { match('[0-9]').repeat }
|
|
104
|
+
rule(:float) { integer >> str('.') >> integer }
|
|
105
|
+
rule(:semicolon) { str(';') }
|
|
106
|
+
rule(:semicolon?) { semicolon.maybe }
|
|
107
|
+
rule(:comma) { str(',') }
|
|
108
|
+
rule(:sign) { str('+') | str('-') }
|
|
109
|
+
rule(:sign?) { sign.maybe }
|
|
110
|
+
rule(:comment) { space? >> (str('/*') >> (str('*/').absent? >> any).repeat >> str('*/')) >> space? }
|
|
111
|
+
rule(:ignore) { comment | space? }
|
|
112
|
+
|
|
113
|
+
# @charset
|
|
114
|
+
# http://www.w3.org/TR/CSS21/syndata.html#charset
|
|
115
|
+
# http://www.iana.org/assignments/character-sets
|
|
116
|
+
@@charsets_file = File.join(File.dirname(__FILE__), '..', '..', 'data', 'iana_character_sets.txt')
|
|
117
|
+
@@charsets = open(@@charsets_file).read.split
|
|
118
|
+
def charsets
|
|
119
|
+
Parslet::Atoms::Alternative.new(*@@charsets.map {|c| str(c)})
|
|
120
|
+
end
|
|
121
|
+
rule(:charset) {
|
|
122
|
+
str('@charset') >> space >> quote >>
|
|
123
|
+
charsets >>
|
|
124
|
+
quote >> semicolon >> ignore
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# @import
|
|
128
|
+
# http://www.w3.org/TR/CSS2/cascade.html#at-import
|
|
129
|
+
rule(:import) {
|
|
130
|
+
str('@import') >> space >> (quote >> name >> str('.css') >> quote | uri) >>
|
|
131
|
+
media_type_list.maybe >> semicolon >> ignore
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# @media
|
|
135
|
+
# http://www.w3.org/TR/CSS2/media.html#media-intro
|
|
136
|
+
rule(:media) {
|
|
137
|
+
ignore >> str('@media') >> media_type_list >> space? >>
|
|
138
|
+
lcurly >> ruleset.repeat >> rcurly
|
|
139
|
+
}
|
|
140
|
+
rule(:media_type_list) {
|
|
141
|
+
space >> media_types >> ((comma >> space? >> media_types).repeat).maybe
|
|
142
|
+
}
|
|
143
|
+
rule(:media_types) {
|
|
144
|
+
str('all') | str('braille') | str('embossed') | str('handheld') |
|
|
145
|
+
str('print') | str('projection') | str('screen') | str('speech') |
|
|
146
|
+
str('tty') | str('tv')
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
rule(:ruleset) { selectors >> lcurly >> declarations >> rcurly }
|
|
150
|
+
# TODO @namespace at-rule
|
|
151
|
+
rule(:stylesheet) { charset.maybe >> import.repeat.maybe >> (media | ruleset).repeat }
|
|
152
|
+
root :stylesheet
|
|
153
|
+
end
|
data/test/parser_test.rb
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'minitest/autorun'
|
|
3
|
+
require 'parslet-css'
|
|
4
|
+
|
|
5
|
+
describe ParsletCSS::Parser do
|
|
6
|
+
before do
|
|
7
|
+
@parser = ParsletCSS::Parser.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "import parse" do
|
|
11
|
+
it "can import other css" do
|
|
12
|
+
@parser.parse('@import "mystyle.css";')
|
|
13
|
+
@parser.parse('@import url("mystyle.css");')
|
|
14
|
+
@parser.parse('@import url("bluish.css") projection, tv;')
|
|
15
|
+
@parser.parse('@import url("fineprint.css") print; @import url("bluish.css") projection, tv; body {}')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
describe "charset" do
|
|
20
|
+
it '@charset rule must place the rule at the very beginning of the style sheet' do
|
|
21
|
+
@parser.parse('@charset "ISO-8859-1";')
|
|
22
|
+
@parser.parse('@charset "UTF-8"; @import "mystyle.css"; body {}')
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe '@media rule' do
|
|
27
|
+
it 'valid statement' do
|
|
28
|
+
@parser.parse('@media print { body { font-size: 10pt } }')
|
|
29
|
+
@parser.parse('@media screen, print { body { font-size: 13px } }')
|
|
30
|
+
@parser.parse('/* print */ @media print { body { font-size: 10pt } }
|
|
31
|
+
/* screen */ @media screen { body { font-size: 13px } }')
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe "parse success" do
|
|
36
|
+
it "parse with percent" do
|
|
37
|
+
@parser.parse("body { height: 100%; width: 100%; }")
|
|
38
|
+
end
|
|
39
|
+
it "with url" do
|
|
40
|
+
@parser.parse("body { background: url(/images/plop.png); }")
|
|
41
|
+
@parser.parse("body { background: url(https://localhost:3000/images/plop.png); }")
|
|
42
|
+
@parser.parse("body { background: url(/~spk/images/plop.png?size=30;toto=tata); }")
|
|
43
|
+
@parser.parse("body { background: url(/~spk/images/plop.png?size=30&toto=tata); }")
|
|
44
|
+
u = @parser.parse("body { background: url(/up.png); } nav { background: url(/nav.png) }")
|
|
45
|
+
assert_equal(2, u.size)
|
|
46
|
+
end
|
|
47
|
+
it "with comments" do
|
|
48
|
+
@parser.parse("body { /* comment */ padding: 0; /* comment */}")
|
|
49
|
+
end
|
|
50
|
+
it "with no space" do
|
|
51
|
+
@parser.parse(":focus{outline:0;/*comment*/}")
|
|
52
|
+
@parser.parse(":focus{outline:0}")
|
|
53
|
+
end
|
|
54
|
+
it "with no last semicolon" do
|
|
55
|
+
@parser.parse("body { height: 100%; width: 100% }")
|
|
56
|
+
end
|
|
57
|
+
it "with one property and no semicolon" do
|
|
58
|
+
@parser.parse("body { width: 100% }")
|
|
59
|
+
end
|
|
60
|
+
it "with blank property" do
|
|
61
|
+
@parser.parse("body { }")
|
|
62
|
+
end
|
|
63
|
+
it 'with color' do
|
|
64
|
+
@parser.parse("body { color: #1a171b; }")
|
|
65
|
+
@parser.parse("body { color: #fff; }")
|
|
66
|
+
end
|
|
67
|
+
it 'with font family' do
|
|
68
|
+
@parser.parse("body { font: 12px monospace, serif; }")
|
|
69
|
+
@parser.parse("body { font: bold 14px/28px Arial; }")
|
|
70
|
+
end
|
|
71
|
+
it 'with multiple property values' do
|
|
72
|
+
@parser.parse("body { margin: 10px 10px 10px 10em;}")
|
|
73
|
+
@parser.parse("body { background: url(/images/ui/down.png) no-repeat scroll 140px 0px transparent; }")
|
|
74
|
+
@parser.parse("body { background: url(/images/ui/up.png) no-repeat scroll 140px 0px #1a171b; }")
|
|
75
|
+
end
|
|
76
|
+
it "float size" do
|
|
77
|
+
@parser.parse(".date_selector .nav { width: 17.5em; }")
|
|
78
|
+
end
|
|
79
|
+
it 'property priority' do
|
|
80
|
+
@parser.parse(".date_selector .nav { width: 17.5em !important; }")
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe('CSS selectors') do
|
|
84
|
+
# http://www.w3.org/TR/CSS2/selector.html
|
|
85
|
+
it "CSS2 w3c examples" do
|
|
86
|
+
# 5.2.1 Grouping
|
|
87
|
+
@parser.parse("h1, h2, h3 { font-family: sans-serif }")
|
|
88
|
+
# 5.6 Child selectors
|
|
89
|
+
@parser.parse("body > P { line-height: 1.3 }")
|
|
90
|
+
@parser.parse("div ol>li p { font: 2px }")
|
|
91
|
+
# 5.7 Adjacent sibling selectors
|
|
92
|
+
@parser.parse("math + p { text-indent: 0 } ")
|
|
93
|
+
@parser.parse("h1 + h2 { margin-top: -5mm }")
|
|
94
|
+
@parser.parse("h1.opener + h2 { margin-top: -5mm }")
|
|
95
|
+
# 5.8.1 Matching attributes and attribute values
|
|
96
|
+
@parser.parse("h1[title] { color: blue; }")
|
|
97
|
+
@parser.parse("span[class=example] { color: blue; }")
|
|
98
|
+
@parser.parse('span[hello="Cleveland"][goodbye="Columbus"] { color: blue; }')
|
|
99
|
+
@parser.parse('a[rel~="copyright"] {}')
|
|
100
|
+
@parser.parse('a[href="http://www.w3.org/"] {}')
|
|
101
|
+
@parser.parse("*[lang=fr] { display : none }")
|
|
102
|
+
@parser.parse('*[lang|="en"] { color : red }')
|
|
103
|
+
@parser.parse('DIALOGUE[character=juliet] { voice-family: "Vivien Leigh", victoria, female }')
|
|
104
|
+
# 5.8.3 Class selectors
|
|
105
|
+
@parser.parse("*.pastoral { color: green }")
|
|
106
|
+
@parser.parse("H1.pastoral { color: green }")
|
|
107
|
+
@parser.parse('p.marine.pastoral { color: green }')
|
|
108
|
+
# 5.9 ID selectors
|
|
109
|
+
@parser.parse("h1#chapter1 { text-align: center }")
|
|
110
|
+
# 5.11 Pseudo-classes
|
|
111
|
+
@parser.parse('div > p:first-child { text-indent: 0 }')
|
|
112
|
+
@parser.parse('p:first-child em { font-weight : bold }')
|
|
113
|
+
@parser.parse('* > a:first-child {} /* A is first child of any element */')
|
|
114
|
+
@parser.parse('a:first-child {} /* Same */')
|
|
115
|
+
@parser.parse(':link { color: red }')
|
|
116
|
+
@parser.parse('a.external:visited { color: blue }')
|
|
117
|
+
@parser.parse('a:focus:hover { background: white }')
|
|
118
|
+
# 5.11.4 The language pseudo-class: :lang
|
|
119
|
+
@parser.parse("html:lang(fr-ca) { quotes: '« ' ' »' }")
|
|
120
|
+
@parser.parse(":lang(fr) > Q { quotes: '« ' ' »' }")
|
|
121
|
+
# 5.12.1 The :first-line pseudo-element
|
|
122
|
+
@parser.parse("p:first-line { text-transform: uppercase }")
|
|
123
|
+
@parser.parse('p:first-letter { font-size: 3em; font-weight: normal }')
|
|
124
|
+
# 5.12.3 The :before and :after pseudo-elements
|
|
125
|
+
@parser.parse('p.special:before {content: "Special! "}')
|
|
126
|
+
@parser.parse('p.special:first-letter {color: #ffd800}')
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# http://www.w3.org/TR/css3-selectors/
|
|
130
|
+
it "CSS3 w3c examples" do
|
|
131
|
+
@parser.parse('object[type^="image/"] {}')
|
|
132
|
+
@parser.parse('a[href$=".html"] {}')
|
|
133
|
+
@parser.parse('tr:nth-child(2n+1) {} /* represents every odd row of an HTML table */')
|
|
134
|
+
@parser.parse('p:nth-child(4n+1) { color: navy; }')
|
|
135
|
+
@parser.parse('button:not([DISABLED]) {}')
|
|
136
|
+
@parser.parse('p::first-line { text-transform: uppercase }')
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
describe "parse fail" do
|
|
142
|
+
# http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
|
|
143
|
+
@raises = [
|
|
144
|
+
{:msg => "with extra semicolon", :css => "body { height: 100%; ; width: 100%; }"},
|
|
145
|
+
{:msg => "with extra curly", :css => "body { height: 100%; width: 100%; }}"},
|
|
146
|
+
{:msg => "& is not valid token", :css => "h3, h4 & h5 {color: red }"},
|
|
147
|
+
{:msg => "1 malformed declaration missing ':', value", :css => "p { color:green; color }"},
|
|
148
|
+
{:msg => "2 malformed declaration missing value", :css => "p { color:green; color: }"},
|
|
149
|
+
{:msg => "unexpected tokens { }", :css => "p { color:green; color{;color:maroon} }"},
|
|
150
|
+
{:msg => "ruleset with unexpected at-keyword @here", :css => "p @here {color: red}"},
|
|
151
|
+
{:msg => "at-rule with unexpected at-keyword @bar", :css => "@foo @bar;"},
|
|
152
|
+
{:msg => "ruleset with unexpected right brace", :css => "}} {{ - }}"},
|
|
153
|
+
{:msg => "ruleset with unexpected right parenthesis", :css => ") ( {} ) p {color: red }"},
|
|
154
|
+
|
|
155
|
+
# http://www.w3.org/TR/CSS2/syndata.html#at-rules
|
|
156
|
+
{:msg => "must ignore any '@import' rule that occurs inside a block",
|
|
157
|
+
:css => '@import "subs.css"; h1 { color: blue } @import "list.css";'},
|
|
158
|
+
{:msg => 'non valid charset', :css => '@charset "none";'},
|
|
159
|
+
{:msg => '@charset rule not at the beginning of the style sheet',
|
|
160
|
+
:css => '@import "awesome.css"; @charset "UTF-8";'},
|
|
161
|
+
|
|
162
|
+
{:msg => 'identifier must not be empty. (Otherwise, the selector is invalid.)',
|
|
163
|
+
:css => 'html:lang() {}'},
|
|
164
|
+
]
|
|
165
|
+
@raises.each do |r|
|
|
166
|
+
class_eval do
|
|
167
|
+
it r[:msg] do
|
|
168
|
+
assert_raises Parslet::ParseFailed do
|
|
169
|
+
@parser.parse(r[:css])
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
end
|
|
176
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: parslet-css
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Laurent Arnoud
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-06-12 00:00:00.000000000Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: parslet
|
|
16
|
+
requirement: &14494580 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '1.2'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *14494580
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: minitest
|
|
27
|
+
requirement: &14493660 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ~>
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *14493660
|
|
36
|
+
description: CSS parser with Parslet
|
|
37
|
+
email: laurent@spkdev.net
|
|
38
|
+
executables: []
|
|
39
|
+
extensions: []
|
|
40
|
+
extra_rdoc_files:
|
|
41
|
+
- README.markdown
|
|
42
|
+
files:
|
|
43
|
+
- LICENSE
|
|
44
|
+
- README.markdown
|
|
45
|
+
- Gemfile
|
|
46
|
+
- data/iana_character_sets.txt
|
|
47
|
+
- data/character-sets
|
|
48
|
+
- lib/parslet-css/parser.rb
|
|
49
|
+
- lib/parslet-css.rb
|
|
50
|
+
- test/parser_test.rb
|
|
51
|
+
homepage: http://github.com/spk/parslet-css
|
|
52
|
+
licenses: []
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
none: false
|
|
59
|
+
requirements:
|
|
60
|
+
- - ! '>='
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
none: false
|
|
65
|
+
requirements:
|
|
66
|
+
- - ! '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
requirements: []
|
|
70
|
+
rubyforge_project:
|
|
71
|
+
rubygems_version: 1.7.2
|
|
72
|
+
signing_key:
|
|
73
|
+
specification_version: 3
|
|
74
|
+
summary: CSS parser with Parslet grammar tool
|
|
75
|
+
test_files:
|
|
76
|
+
- test/parser_test.rb
|