csspool 0.1.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.
@@ -0,0 +1,29 @@
1
+ module CSS
2
+ module SAC
3
+ class Lexeme
4
+ attr_reader :name, :pattern
5
+
6
+ def initialize(name, pattern=nil, &block)
7
+ raise ArgumentError, "name required" unless name
8
+
9
+ @name = name
10
+ patterns = []
11
+
12
+ patterns << pattern if pattern
13
+ yield(patterns) if block_given?
14
+
15
+ if patterns.empty?
16
+ raise ArgumentError, "at least one pattern required"
17
+ end
18
+
19
+ patterns.collect! do |pattern|
20
+ source = pattern.source
21
+ source = "\\A#{source}"
22
+ Regexp.new(source, Regexp::IGNORECASE + Regexp::MULTILINE)
23
+ end
24
+
25
+ @pattern = Regexp.union(*patterns)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,111 @@
1
+ module CSS
2
+ module SAC
3
+ class LexicalUnit
4
+ attr_accessor :dimension_unit_text,
5
+ :lexical_unit_type,
6
+ :float_value,
7
+ :integer_value,
8
+ :string_value,
9
+ :parameters,
10
+ :function_name
11
+
12
+ alias :to_s :string_value
13
+ end
14
+
15
+ class Function < LexicalUnit
16
+ FUNCTIONS = {
17
+ 'counter' => :SAC_COUNTER_FUNCTION,
18
+ 'counters' => :SAC_COUNTERS_FUNCTION,
19
+ 'rect' => :SAC_RECT_FUNCTION,
20
+ }
21
+ def initialize(name, params)
22
+ self.string_value = "#{name}#{params.join(', ')})"
23
+ name =~ /^(.*)\(/
24
+ self.function_name = $1
25
+ self.parameters = params
26
+ self.lexical_unit_type = FUNCTIONS[self.function_name] || :SAC_FUNCTION
27
+ end
28
+ end
29
+
30
+ class Color < LexicalUnit
31
+ def initialize(value)
32
+ self.string_value = value
33
+ self.lexical_unit_type = :SAC_RGBCOLOR
34
+ if value =~ /^#(\d{1,2})(\d{1,2})(\d{1,2})$/
35
+ self.parameters = [
36
+ Number.new($1.hex, '', :SAC_INTEGER),
37
+ Number.new($2.hex, '', :SAC_INTEGER),
38
+ Number.new($3.hex, '', :SAC_INTEGER)
39
+ ]
40
+ else
41
+ self.parameters = [LexicalIdent.new(value)]
42
+ end
43
+ end
44
+ end
45
+
46
+ class LexicalString < LexicalUnit
47
+ def initialize(value)
48
+ self.string_value = value
49
+ self.lexical_unit_type = :SAC_STRING_VALUE
50
+ end
51
+ end
52
+
53
+ class LexicalIdent < LexicalUnit
54
+ def initialize(value)
55
+ self.string_value = value
56
+ self.lexical_unit_type = :SAC_IDENT
57
+ end
58
+ end
59
+
60
+ class LexicalURI < LexicalUnit
61
+ def initialize(value)
62
+ self.string_value = value.gsub(/^url\(/, '').gsub(/\)$/, '')
63
+ self.lexical_unit_type = :SAC_URI
64
+ end
65
+ end
66
+
67
+ class Number < LexicalUnit
68
+ NON_NEGATIVE_UNITS = [
69
+ :SAC_DEGREE,
70
+ :SAC_GRADIAN,
71
+ :SAC_RADIAN,
72
+ :SAC_MILLISECOND,
73
+ :SAC_SECOND,
74
+ :SAC_HERTZ,
75
+ :SAC_KILOHERTZ,
76
+ ]
77
+ UNITS = {
78
+ 'deg' => :SAC_DEGREE,
79
+ 'rad' => :SAC_RADIAN,
80
+ 'grad' => :SAC_GRADIAN,
81
+ 'ms' => :SAC_MILLISECOND,
82
+ 's' => :SAC_SECOND,
83
+ 'hz' => :SAC_HERTZ,
84
+ 'khz' => :SAC_KILOHERTZ,
85
+ 'px' => :SAC_PIXEL,
86
+ 'cm' => :SAC_CENTIMETER,
87
+ 'mm' => :SAC_MILLIMETER,
88
+ 'in' => :SAC_INCH,
89
+ 'pt' => :SAC_POINT,
90
+ 'pc' => :SAC_PICA,
91
+ '%' => :SAC_PERCENTAGE,
92
+ 'em' => :SAC_EM,
93
+ 'ex' => :SAC_EX,
94
+ }
95
+ def initialize(value, unit = nil, type = nil)
96
+ if value.is_a?(String)
97
+ value =~ /^(-?[0-9.]*)(.*)$/
98
+ value = $1
99
+ unit ||= $2
100
+ end
101
+ type ||= UNITS[self.dimension_unit_text]
102
+ self.string_value = "#{value}#{unit}"
103
+ self.float_value = value.to_f
104
+ self.integer_value = value.to_i
105
+ self.dimension_unit_text = unit.downcase
106
+ self.lexical_unit_type = UNITS[self.dimension_unit_text] ||
107
+ (value =~ /\./ ? :SAC_NUMBER : :SAC_INTEGER)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,6 @@
1
+ module CSS
2
+ module SAC
3
+ class ParseException < RuntimeError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,98 @@
1
+ require "css/sac/document_handler"
2
+ require "css/sac/error_handler"
3
+ require "css/sac/generated_parser"
4
+ require "css/sac/lexical_unit"
5
+ require "css/sac/parse_exception"
6
+ require "css/sac/tokenizer"
7
+ require "css/sac/property_parser"
8
+ require "css/sac/condition"
9
+ require "css/sac/attribute_condition"
10
+ require "css/sac/selectors"
11
+
12
+ module CSS
13
+ module SAC
14
+ class Parser < CSS::SAC::GeneratedParser
15
+ # The version of CSSPool you're using
16
+ VERSION = '0.1.0'
17
+
18
+ TOKENIZER = Tokenizer.new
19
+
20
+ attr_accessor :document_handler, :error_handler, :logger
21
+
22
+ def initialize(document_handler = nil)
23
+ @error_handler = ErrorHandler.new
24
+ @document_handler = document_handler || DocumentHandler.new()
25
+ @property_parser = PropertyParser.new()
26
+ @tokenizer = TOKENIZER
27
+ @logger = nil
28
+ end
29
+
30
+ def parse_style_sheet(string)
31
+ @yydebug = true
32
+ @tokens = TOKENIZER.tokenize(string)
33
+ @position = 0
34
+
35
+ self.document_handler.start_document(string)
36
+ do_parse
37
+ self.document_handler.end_document(string)
38
+ end
39
+
40
+ alias :parse :parse_style_sheet
41
+ alias :parse_rule :parse_style_sheet
42
+
43
+ # Returns the parser version. We return CSS2, but its actually
44
+ # CSS2.1. No font-face tags. Sorry.
45
+ def parser_version
46
+ "http://www.w3.org/TR/REC-CSS2"
47
+ end
48
+
49
+ attr_reader :property_parser
50
+ attr_reader :tokenizer
51
+
52
+ private # Bro.
53
+
54
+ # We have to eliminate matching pairs.
55
+ # http://www.w3.org/TR/CSS21/syndata.html#parsing-errors
56
+ # See the malformed declarations section
57
+ def eliminate_pair_matches(error_value)
58
+ pairs = {}
59
+ pairs['"'] = '"'
60
+ pairs["'"] = "'"
61
+ pairs['{'] = '}'
62
+ pairs['['] = ']'
63
+ pairs['('] = ')'
64
+
65
+ error_value.strip!
66
+ if pairs[error_value]
67
+ logger.warn("Eliminating pair for: #{error_value}") if logger
68
+ loop {
69
+ token = next_token
70
+ eliminate_pair_matches(token[1])
71
+ logger.warn("Eliminated token: #{token.join(' ')}") if logger
72
+ break if token[1] == pairs[error_value]
73
+ }
74
+ end
75
+ end
76
+
77
+ def on_error(error_token_id, error_value, value_stack)
78
+ if logger
79
+ logger.error(token_to_str(error_token_id))
80
+ logger.error("error value: #{error_value}")
81
+ end
82
+ eliminate_pair_matches(error_value)
83
+ end
84
+
85
+ def next_token
86
+ return [false, false] if @position >= @tokens.length
87
+
88
+ n_token = @tokens[@position]
89
+ @position += 1
90
+ if n_token.name == :COMMENT
91
+ self.document_handler.comment(n_token.value)
92
+ return next_token
93
+ end
94
+ n_token.to_racc_token
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,47 @@
1
+ require "css/sac/generated_property_parser"
2
+
3
+ module CSS
4
+ module SAC
5
+ class PropertyParser < CSS::SAC::GeneratedPropertyParser
6
+ def initialize
7
+ @tokens = []
8
+ @token_table = Racc_arg[10]
9
+ end
10
+
11
+ def parse_tokens(tokens)
12
+ negate = false # Nasty hack for unary minus
13
+ @tokens = tokens.find_all { |x| x.name != :S }.map { |token|
14
+ tok = if @token_table.has_key?(token.value)
15
+ [token.value, token.value]
16
+ else
17
+ if token.name == :delim && !@token_table.has_key?(token.value)
18
+ negate = true if token.value == '-'
19
+ nil
20
+ else
21
+ token.to_racc_token
22
+ end
23
+ end
24
+
25
+ if negate && tok
26
+ tok[1] = "-#{tok[1]}"
27
+ negate = false
28
+ end
29
+
30
+ tok
31
+ }.compact
32
+
33
+ begin
34
+ return do_parse
35
+ rescue ParseError => e
36
+ return nil
37
+ end
38
+ end
39
+
40
+ private
41
+ def next_token
42
+ return [false, false] if @tokens.empty?
43
+ @tokens.shift
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,87 @@
1
+ module CSS
2
+ module SAC
3
+ class Selector
4
+ attr_reader :selector_type
5
+ end
6
+
7
+ class SimpleSelector < Selector
8
+ def initialize
9
+ @selector_type = :SAC_ANY_NODE_SELECTOR
10
+ end
11
+
12
+ def to_css
13
+ case @selector_type
14
+ when :SAC_ANY_NODE_SELECTOR
15
+ '*'
16
+ end
17
+ end
18
+ end
19
+
20
+ class ElementSelector < SimpleSelector
21
+ attr_reader :local_name
22
+ alias :name :local_name
23
+
24
+ def initialize(name)
25
+ super()
26
+ @selector_type = :SAC_ELEMENT_NODE_SELECTOR
27
+ @local_name = name
28
+ end
29
+
30
+ def to_css
31
+ local_name
32
+ end
33
+ end
34
+
35
+ class ConditionalSelector < SimpleSelector
36
+ attr_accessor :condition, :simple_selector
37
+ alias :selector :simple_selector
38
+
39
+ def initialize(selector, condition)
40
+ @condition = condition
41
+ @simple_selector = selector
42
+ @selector_type = :SAC_CONDITIONAL_SELECTOR
43
+ end
44
+
45
+ def to_css
46
+ [selector, condition].map { |x|
47
+ x ? x.to_css : ''
48
+ }.join('')
49
+ end
50
+ end
51
+
52
+ class DescendantSelector < SimpleSelector
53
+ attr_accessor :ancestor_selector, :simple_selector
54
+ alias :selector :simple_selector
55
+
56
+ def initialize(ancestor, selector, type)
57
+ @ancestor_selector = ancestor
58
+ @simple_selector = selector
59
+ @selector_type = type
60
+ end
61
+
62
+ def to_css
63
+ descent_token =
64
+ case @selector_type
65
+ when :SAC_CHILD_SELECTOR
66
+ ' > '
67
+ when :SAC_DESCENDANT_SELECTOR
68
+ ' '
69
+ end
70
+ ancestor_selector.to_css + descent_token + selector.to_css
71
+ end
72
+ end
73
+
74
+ class SiblingSelector < SimpleSelector
75
+ attr_accessor :selector, :sibling_selector
76
+ def initialize(selector, sibling)
77
+ @selector = selector
78
+ @sibling_selector = sibling
79
+ @selector_type = :SAC_DIRECT_ADJACENT_SELECTOR
80
+ end
81
+
82
+ def to_css
83
+ selector.to_css + ' + ' + sibling_selector.to_css
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,27 @@
1
+ module CSS
2
+ module SAC
3
+ class Token
4
+ attr_reader :name, :value, :position
5
+
6
+ def initialize(name, value, position)
7
+ @name = name
8
+ @value = value
9
+ @position = position
10
+ end
11
+
12
+ def to_racc_token
13
+ [name, value]
14
+ end
15
+ end
16
+
17
+ class DelimiterToken < Token
18
+ def initialize(value, position)
19
+ super(:delim, value, position)
20
+ end
21
+
22
+ def to_racc_token
23
+ [value, value]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,186 @@
1
+ require "css/sac/lexeme"
2
+ require "css/sac/token"
3
+
4
+ module CSS
5
+ module SAC
6
+ class Tokenizer
7
+ def initialize(&block)
8
+ @lexemes = []
9
+ @macros = {}
10
+
11
+ # http://www.w3.org/TR/CSS21/syndata.html
12
+ macro(:h, /([0-9a-f])/ )
13
+ macro(:nonascii, /([\200-\377])/ )
14
+ macro(:nl, /(\n|\r\n|\r|\f)/ )
15
+ macro(:unicode, /(\\#{m(:h)}{1,6}(\r\n|[ \t\r\n\f])?)/ )
16
+ macro(:escape, /(#{m(:unicode)}|\\[^\r\n\f0-9a-f])/ )
17
+ macro(:nmstart, /([_a-z]|#{m(:nonascii)}|#{m(:escape)})/ )
18
+ macro(:nmchar, /([_a-z0-9-]|#{m(:nonascii)}|#{m(:escape)})/ )
19
+ macro(:string1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*\")/ )
20
+ macro(:string2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*\')/ )
21
+ macro(:invalid1, /(\"([^\n\r\f\\\"]|\\#{m(:nl)}|#{m(:escape)})*)/ )
22
+ macro(:invalid2, /(\'([^\n\r\f\\']|\\#{m(:nl)}|#{m(:escape)})*)/ )
23
+ macro(:comment, /(\/\*[^*]*\*+([^\/*][^*]*\*+)*\/)/ )
24
+ macro(:ident, /(-?#{m(:nmstart)}#{m(:nmchar)}*)/ )
25
+ macro(:name, /(#{m(:nmchar)}+)/ )
26
+ macro(:num, /([0-9]+|[0-9]*\.[0-9]+)/ )
27
+ macro(:string, /(#{m(:string1)}|#{m(:string2)})/ )
28
+ macro(:invalid, /(#{m(:invalid1)}|#{m(:invalid2)})/ )
29
+ macro(:url, /(([!#\$%&*-~]|#{m(:nonascii)}|#{m(:escape)})*)/ )
30
+ macro(:s, /([ \t\r\n\f]+)/ )
31
+ macro(:w, /(#{m(:s)}?)/ )
32
+ macro(:A, /(a|\\0{0,4}(41|61)(\r\n|[ \t\r\n\f])?)/ )
33
+ macro(:C, /(c|\\0{0,4}(43|63)(\r\n|[ \t\r\n\f])?)/ )
34
+ macro(:D, /(d|\\0{0,4}(44|64)(\r\n|[ \t\r\n\f])?)/ )
35
+ macro(:E, /(e|\\0{0,4}(45|65)(\r\n|[ \t\r\n\f])?)/ )
36
+ macro(:G, /(g|\\0{0,4}(47|67)(\r\n|[ \t\r\n\f])?|\\g)/ )
37
+ macro(:H, /(h|\\0{0,4}(48|68)(\r\n|[ \t\r\n\f])?|\\h)/ )
38
+ macro(:I, /(i|\\0{0,4}(49|69)(\r\n|[ \t\r\n\f])?|\\i)/ )
39
+ macro(:K, /(k|\\0{0,4}(4b|6b)(\r\n|[ \t\r\n\f])?|\\k)/ )
40
+ macro(:M, /(m|\\0{0,4}(4d|6d)(\r\n|[ \t\r\n\f])?|\\m)/ )
41
+ macro(:N, /(n|\\0{0,4}(4e|6e)(\r\n|[ \t\r\n\f])?|\\n)/ )
42
+ macro(:O, /(o|\\0{0,4}(51|71)(\r\n|[ \t\r\n\f])?|\\o)/ )
43
+ macro(:P, /(p|\\0{0,4}(50|70)(\r\n|[ \t\r\n\f])?|\\p)/ )
44
+ macro(:R, /(r|\\0{0,4}(52|72)(\r\n|[ \t\r\n\f])?|\\r)/ )
45
+ macro(:S, /(s|\\0{0,4}(53|73)(\r\n|[ \t\r\n\f])?|\\s)/ )
46
+ macro(:T, /(t|\\0{0,4}(54|74)(\r\n|[ \t\r\n\f])?|\\t)/ )
47
+ macro(:X, /(x|\\0{0,4}(58|78)(\r\n|[ \t\r\n\f])?|\\x)/ )
48
+ macro(:Z, /(z|\\0{0,4}(5a|7a)(\r\n|[ \t\r\n\f])?|\\z)/ )
49
+
50
+ #token :COMMENT do |patterns|
51
+ # patterns << /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
52
+ # patterns << /#{m(:s)}+\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
53
+ #end
54
+
55
+ token(:LBRACE, /#{m(:w)}\{/)
56
+ token(:PLUS, /#{m(:w)}\+/)
57
+ token(:GREATER, /#{m(:w)}>/)
58
+ token(:COMMA, /#{m(:w)},/)
59
+
60
+ token(:S, /#{m(:s)}/)
61
+
62
+ #token :URI do |patterns|
63
+ # patterns << /url\(#{m(:w)}#{m(:string)}#{m(:w)}\)/
64
+ # patterns << /url\(#{m(:w)}#{m(:url)}#{m(:w)}\)/
65
+ #end
66
+
67
+ token(:FUNCTION, /#{m(:ident)}\(/)
68
+ token(:IDENT, /#{m(:ident)}/)
69
+
70
+ token(:CDO, /<!--/)
71
+ token(:CDC, /-->/)
72
+ token(:INCLUDES, /~=/)
73
+ token(:DASHMATCH, /\|=/)
74
+ #token(:STRING, /#{m(:string)}/)
75
+ token(:INVALID, /#{m(:invalid)}/)
76
+ token(:HASH, /##{m(:name)}/)
77
+ token(:IMPORT_SYM, /@#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}/)
78
+ token(:PAGE_SYM, /@#{m(:P)}#{m(:A)}#{m(:G)}#{m(:E)}/)
79
+ token(:MEDIA_SYM, /@#{m(:M)}#{m(:E)}#{m(:D)}#{m(:I)}#{m(:A)}/)
80
+ token(:CHARSET_SYM, /@#{m(:C)}#{m(:H)}#{m(:A)}#{m(:R)}#{m(:S)}#{m(:E)}#{m(:T)}/)
81
+ token(:IMPORTANT_SYM, /!(#{m(:w)}|#{m(:comment)})*#{m(:I)}#{m(:M)}#{m(:P)}#{m(:O)}#{m(:R)}#{m(:T)}#{m(:A)}#{m(:N)}#{m(:T)}/)
82
+ token(:EMS, /#{m(:num)}#{m(:E)}#{m(:M)}/)
83
+ token(:EXS, /#{m(:num)}#{m(:E)}#{m(:X)}/)
84
+
85
+ token :LENGTH do |patterns|
86
+ patterns << /#{m(:num)}#{m(:P)}#{m(:X)}/
87
+ patterns << /#{m(:num)}#{m(:C)}#{m(:M)}/
88
+ patterns << /#{m(:num)}#{m(:M)}#{m(:M)}/
89
+ patterns << /#{m(:num)}#{m(:I)}#{m(:N)}/
90
+ patterns << /#{m(:num)}#{m(:P)}#{m(:T)}/
91
+ patterns << /#{m(:num)}#{m(:P)}#{m(:C)}/
92
+ end
93
+
94
+ token :ANGLE do |patterns|
95
+ patterns << /#{m(:num)}#{m(:D)}#{m(:E)}#{m(:G)}/
96
+ patterns << /#{m(:num)}#{m(:R)}#{m(:A)}#{m(:D)}/
97
+ patterns << /#{m(:num)}#{m(:G)}#{m(:R)}#{m(:A)}#{m(:D)}/
98
+ end
99
+
100
+ token :TIME do |patterns|
101
+ patterns << /#{m(:num)}#{m(:M)}#{m(:S)}/
102
+ patterns << /#{m(:num)}#{m(:S)}/
103
+ end
104
+
105
+ token :FREQ do |patterns|
106
+ patterns << /#{m(:num)}#{m(:H)}#{m(:Z)}/
107
+ patterns << /#{m(:num)}#{m(:K)}#{m(:H)}#{m(:Z)}/
108
+ end
109
+
110
+ token(:DIMENSION, /#{m(:num)}#{m(:ident)}/)
111
+ token(:PERCENTAGE, /#{m(:num)}%/)
112
+ token(:NUMBER, /#{m(:num)}/)
113
+
114
+
115
+ yield self if block_given?
116
+ end
117
+
118
+ def tokenize(input_data)
119
+ tokens = []
120
+ pos = 0
121
+
122
+ comments = input_data.scan(/\/\*[^*]*\*+\//m)
123
+ non_comments = input_data.split(/\/\*[^*]*\*+\//m)
124
+
125
+ # Handle a small edge case, if our CSS is *only* comments,
126
+ # the split, zip, scan trick won't work
127
+ if non_comments.length == 0
128
+ tokens = comments.map { |x| Token.new(:COMMENT, x, nil) }
129
+ else
130
+ non_comments.zip(comments).each do |non_comment, comment|
131
+ non_comment.split(/url\([^\)]*\)/m).zip(
132
+ non_comment.scan(/url\([^\)]*\)/m)
133
+ ).each do |non_url, url|
134
+ non_url.split(/"[^"]*"|'[^']*'/m).zip(
135
+ non_url.scan(/"[^"]*"|'[^']*'/m)
136
+ ).each do |non_string, quoted_string|
137
+ if non_string.length > 0 && non_string =~ /\A\s*\Z/m
138
+ tokens << Token.new(:S, non_string, nil)
139
+ else
140
+ non_string.split(/[ \t\r\n\f]*(?![{}+>]*)/m).zip(
141
+ non_string.scan(/[ \t\r\n\f]*(?![{}+>]*)/m)
142
+ ).each do |string, whitespace|
143
+ until string.empty?
144
+ token = nil
145
+ @lexemes.each do |lexeme|
146
+ match = lexeme.pattern.match(string)
147
+ if match
148
+ token = Token.new(lexeme.name, match.to_s, pos)
149
+ break
150
+ end
151
+ end
152
+
153
+ token ||= DelimiterToken.new(/^./.match(string).to_s, pos)
154
+
155
+ tokens << token
156
+ string = string.slice(Range.new(token.value.length, -1))
157
+ pos += token.value.length
158
+ end
159
+ tokens << Token.new(:S, whitespace, nil) if whitespace
160
+ end
161
+ end
162
+ tokens << Token.new(:STRING, quoted_string, nil) if quoted_string
163
+ end
164
+ tokens << Token.new(:URI, url, nil) if url
165
+ end
166
+ tokens << Token.new(:COMMENT, comment, nil) if comment
167
+ end
168
+ end
169
+
170
+ tokens
171
+ end
172
+
173
+ private
174
+
175
+ def token(name, pattern=nil, &block)
176
+ @lexemes << Lexeme.new(name, pattern, &block)
177
+ end
178
+
179
+ def macro(name, regex=nil)
180
+ regex ? @macros[name] = regex : @macros[name].source
181
+ end
182
+
183
+ alias :m :macro
184
+ end
185
+ end
186
+ end
data/lib/css/sac.rb ADDED
@@ -0,0 +1,13 @@
1
+ require "css/sac/parser"
2
+
3
+ module CSS
4
+ module SAC
5
+ class << self
6
+ def parse(text)
7
+ parser = CSS::SAC::Parser.new
8
+ parser.parse(text)
9
+ parser
10
+ end
11
+ end
12
+ end
13
+ end