haml 3.0.25 → 3.1.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/.yardopts +1 -1
- data/CONTRIBUTING +0 -1
- data/EDGE_GEM_VERSION +1 -0
- data/MIT-LICENSE +1 -1
- data/README.md +10 -175
- data/Rakefile +56 -84
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/init.rb +1 -1
- data/lib/haml.rb +14 -12
- data/lib/haml/engine.rb +1 -1
- data/lib/haml/exec.rb +19 -316
- data/lib/haml/helpers/action_view_extensions.rb +1 -1
- data/lib/haml/html.rb +69 -76
- data/lib/haml/precompiler.rb +34 -41
- data/lib/haml/railtie.rb +4 -6
- data/lib/haml/template/plugin.rb +6 -16
- data/lib/haml/util.rb +91 -107
- data/lib/haml/version.rb +7 -0
- data/test/benchmark.rb +2 -9
- data/test/haml/engine_test.rb +195 -92
- data/test/haml/html2haml/erb_tests.rb +0 -14
- data/test/haml/util_test.rb +32 -0
- data/test/test_helper.rb +0 -39
- metadata +96 -324
- data/bin/css2sass +0 -13
- data/bin/sass +0 -8
- data/bin/sass-convert +0 -7
- data/extra/haml-mode.el +0 -753
- data/extra/sass-mode.el +0 -207
- data/lib/haml/util/subset_map.rb +0 -101
- data/lib/sass.rb +0 -29
- data/lib/sass/callbacks.rb +0 -52
- data/lib/sass/css.rb +0 -294
- data/lib/sass/engine.rb +0 -720
- data/lib/sass/environment.rb +0 -143
- data/lib/sass/error.rb +0 -198
- data/lib/sass/files.rb +0 -160
- data/lib/sass/less.rb +0 -382
- data/lib/sass/plugin.rb +0 -279
- data/lib/sass/plugin/configuration.rb +0 -221
- data/lib/sass/plugin/generic.rb +0 -15
- data/lib/sass/plugin/merb.rb +0 -37
- data/lib/sass/plugin/rack.rb +0 -47
- data/lib/sass/plugin/rails.rb +0 -32
- data/lib/sass/plugin/staleness_checker.rb +0 -123
- data/lib/sass/repl.rb +0 -58
- data/lib/sass/script.rb +0 -63
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/color.rb +0 -491
- data/lib/sass/script/css_lexer.rb +0 -29
- data/lib/sass/script/css_parser.rb +0 -31
- data/lib/sass/script/funcall.rb +0 -77
- data/lib/sass/script/functions.rb +0 -861
- data/lib/sass/script/interpolation.rb +0 -70
- data/lib/sass/script/lexer.rb +0 -337
- data/lib/sass/script/literal.rb +0 -236
- data/lib/sass/script/node.rb +0 -112
- data/lib/sass/script/number.rb +0 -423
- data/lib/sass/script/operation.rb +0 -95
- data/lib/sass/script/parser.rb +0 -401
- data/lib/sass/script/string.rb +0 -67
- data/lib/sass/script/string_interpolation.rb +0 -93
- data/lib/sass/script/unary_operation.rb +0 -57
- data/lib/sass/script/variable.rb +0 -48
- data/lib/sass/scss.rb +0 -17
- data/lib/sass/scss/css_parser.rb +0 -46
- data/lib/sass/scss/parser.rb +0 -855
- data/lib/sass/scss/rx.rb +0 -126
- data/lib/sass/scss/sass_parser.rb +0 -11
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
- data/lib/sass/scss/static_parser.rb +0 -40
- data/lib/sass/selector.rb +0 -361
- data/lib/sass/selector/abstract_sequence.rb +0 -62
- data/lib/sass/selector/comma_sequence.rb +0 -82
- data/lib/sass/selector/sequence.rb +0 -237
- data/lib/sass/selector/simple.rb +0 -113
- data/lib/sass/selector/simple_sequence.rb +0 -136
- data/lib/sass/tree/charset_node.rb +0 -37
- data/lib/sass/tree/comment_node.rb +0 -128
- data/lib/sass/tree/debug_node.rb +0 -36
- data/lib/sass/tree/directive_node.rb +0 -75
- data/lib/sass/tree/extend_node.rb +0 -65
- data/lib/sass/tree/for_node.rb +0 -55
- data/lib/sass/tree/if_node.rb +0 -69
- data/lib/sass/tree/import_node.rb +0 -102
- data/lib/sass/tree/mixin_def_node.rb +0 -48
- data/lib/sass/tree/mixin_node.rb +0 -111
- data/lib/sass/tree/node.rb +0 -464
- data/lib/sass/tree/prop_node.rb +0 -220
- data/lib/sass/tree/root_node.rb +0 -163
- data/lib/sass/tree/rule_node.rb +0 -261
- data/lib/sass/tree/variable_node.rb +0 -39
- data/lib/sass/tree/warn_node.rb +0 -42
- data/lib/sass/tree/while_node.rb +0 -36
- data/test/haml/util/subset_map_test.rb +0 -91
- data/test/sass/callbacks_test.rb +0 -61
- data/test/sass/conversion_test.rb +0 -1218
- data/test/sass/css2sass_test.rb +0 -364
- data/test/sass/data/hsl-rgb.txt +0 -319
- data/test/sass/engine_test.rb +0 -2267
- data/test/sass/extend_test.rb +0 -1348
- data/test/sass/functions_test.rb +0 -556
- data/test/sass/less_conversion_test.rb +0 -653
- data/test/sass/more_results/more1.css +0 -9
- data/test/sass/more_results/more1_with_line_comments.css +0 -26
- data/test/sass/more_results/more_import.css +0 -29
- data/test/sass/more_templates/_more_partial.sass +0 -2
- data/test/sass/more_templates/more1.sass +0 -23
- data/test/sass/more_templates/more_import.sass +0 -11
- data/test/sass/plugin_test.rb +0 -433
- data/test/sass/results/alt.css +0 -4
- data/test/sass/results/basic.css +0 -9
- data/test/sass/results/compact.css +0 -5
- data/test/sass/results/complex.css +0 -86
- data/test/sass/results/compressed.css +0 -1
- data/test/sass/results/expanded.css +0 -19
- data/test/sass/results/import.css +0 -31
- data/test/sass/results/import_charset.css +0 -4
- data/test/sass/results/import_charset_1_8.css +0 -4
- data/test/sass/results/import_charset_ibm866.css +0 -4
- data/test/sass/results/line_numbers.css +0 -49
- data/test/sass/results/mixins.css +0 -95
- data/test/sass/results/multiline.css +0 -24
- data/test/sass/results/nested.css +0 -22
- data/test/sass/results/options.css +0 -1
- data/test/sass/results/parent_ref.css +0 -13
- data/test/sass/results/script.css +0 -16
- data/test/sass/results/scss_import.css +0 -31
- data/test/sass/results/scss_importee.css +0 -2
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +0 -1
- data/test/sass/results/subdir/subdir.css +0 -3
- data/test/sass/results/units.css +0 -11
- data/test/sass/results/warn.css +0 -0
- data/test/sass/results/warn_imported.css +0 -0
- data/test/sass/script_conversion_test.rb +0 -314
- data/test/sass/script_test.rb +0 -470
- data/test/sass/scss/css_test.rb +0 -916
- data/test/sass/scss/rx_test.rb +0 -156
- data/test/sass/scss/scss_test.rb +0 -1122
- data/test/sass/scss/test_helper.rb +0 -37
- data/test/sass/templates/_imported_charset_ibm866.sass +0 -4
- data/test/sass/templates/_imported_charset_utf8.sass +0 -4
- data/test/sass/templates/_partial.sass +0 -2
- data/test/sass/templates/alt.sass +0 -16
- data/test/sass/templates/basic.sass +0 -23
- data/test/sass/templates/bork1.sass +0 -2
- data/test/sass/templates/bork2.sass +0 -2
- data/test/sass/templates/bork3.sass +0 -2
- data/test/sass/templates/bork4.sass +0 -2
- data/test/sass/templates/compact.sass +0 -17
- data/test/sass/templates/complex.sass +0 -305
- data/test/sass/templates/compressed.sass +0 -15
- data/test/sass/templates/expanded.sass +0 -17
- data/test/sass/templates/import.sass +0 -12
- data/test/sass/templates/import_charset.sass +0 -7
- data/test/sass/templates/import_charset_1_8.sass +0 -4
- data/test/sass/templates/import_charset_ibm866.sass +0 -9
- data/test/sass/templates/importee.less +0 -2
- data/test/sass/templates/importee.sass +0 -19
- data/test/sass/templates/line_numbers.sass +0 -13
- data/test/sass/templates/mixin_bork.sass +0 -5
- data/test/sass/templates/mixins.sass +0 -76
- data/test/sass/templates/multiline.sass +0 -20
- data/test/sass/templates/nested.sass +0 -25
- data/test/sass/templates/nested_bork1.sass +0 -2
- data/test/sass/templates/nested_bork2.sass +0 -2
- data/test/sass/templates/nested_bork3.sass +0 -2
- data/test/sass/templates/nested_bork4.sass +0 -2
- data/test/sass/templates/nested_mixin_bork.sass +0 -6
- data/test/sass/templates/options.sass +0 -2
- data/test/sass/templates/parent_ref.sass +0 -25
- data/test/sass/templates/script.sass +0 -101
- data/test/sass/templates/scss_import.scss +0 -11
- data/test/sass/templates/scss_importee.scss +0 -1
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +0 -2
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +0 -3
- data/test/sass/templates/subdir/subdir.sass +0 -6
- data/test/sass/templates/units.sass +0 -11
- data/test/sass/templates/warn.sass +0 -3
- data/test/sass/templates/warn_imported.sass +0 -4
- data/vendor/fssm/LICENSE +0 -20
- data/vendor/fssm/README.markdown +0 -55
- data/vendor/fssm/Rakefile +0 -59
- data/vendor/fssm/VERSION.yml +0 -5
- data/vendor/fssm/example.rb +0 -9
- data/vendor/fssm/fssm.gemspec +0 -77
- data/vendor/fssm/lib/fssm.rb +0 -33
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +0 -36
- data/vendor/fssm/lib/fssm/backends/inotify.rb +0 -26
- data/vendor/fssm/lib/fssm/backends/polling.rb +0 -25
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +0 -131
- data/vendor/fssm/lib/fssm/monitor.rb +0 -26
- data/vendor/fssm/lib/fssm/path.rb +0 -91
- data/vendor/fssm/lib/fssm/pathname.rb +0 -502
- data/vendor/fssm/lib/fssm/state/directory.rb +0 -57
- data/vendor/fssm/lib/fssm/state/file.rb +0 -24
- data/vendor/fssm/lib/fssm/support.rb +0 -63
- data/vendor/fssm/lib/fssm/tree.rb +0 -176
- data/vendor/fssm/profile/prof-cache.rb +0 -40
- data/vendor/fssm/profile/prof-fssm-pathname.html +0 -1231
- data/vendor/fssm/profile/prof-pathname.rb +0 -68
- data/vendor/fssm/profile/prof-plain-pathname.html +0 -988
- data/vendor/fssm/profile/prof.html +0 -2379
- data/vendor/fssm/spec/path_spec.rb +0 -75
- data/vendor/fssm/spec/root/duck/quack.txt +0 -0
- data/vendor/fssm/spec/root/file.css +0 -0
- data/vendor/fssm/spec/root/file.rb +0 -0
- data/vendor/fssm/spec/root/file.yml +0 -0
- data/vendor/fssm/spec/root/moo/cow.txt +0 -0
- data/vendor/fssm/spec/spec_helper.rb +0 -14
data/lib/sass/script/string.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'sass/script/literal'
|
2
|
-
|
3
|
-
module Sass::Script
|
4
|
-
# A SassScript object representing a CSS string *or* a CSS identifier.
|
5
|
-
class String < Literal
|
6
|
-
# The Ruby value of the string.
|
7
|
-
#
|
8
|
-
# @return [String]
|
9
|
-
attr_reader :value
|
10
|
-
|
11
|
-
# Whether this is a CSS string or a CSS identifier.
|
12
|
-
# The difference is that strings are written with double-quotes,
|
13
|
-
# while identifiers aren't.
|
14
|
-
#
|
15
|
-
# @return [Symbol] `:string` or `:identifier`
|
16
|
-
attr_reader :type
|
17
|
-
|
18
|
-
# In addition to setting the \{#context} of the string,
|
19
|
-
# this sets the string to be an identifier if the context is `:equals`.
|
20
|
-
#
|
21
|
-
# @see Node#context=
|
22
|
-
def context=(context)
|
23
|
-
super
|
24
|
-
@type = :identifier if context == :equals
|
25
|
-
end
|
26
|
-
|
27
|
-
# Creates a new string.
|
28
|
-
#
|
29
|
-
# @param value [String] See \{#value}
|
30
|
-
# @param type [Symbol] See \{#type}
|
31
|
-
def initialize(value, type = :identifier)
|
32
|
-
super(value)
|
33
|
-
@type = type
|
34
|
-
end
|
35
|
-
|
36
|
-
# @see Literal#plus
|
37
|
-
def plus(other)
|
38
|
-
other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
|
39
|
-
Sass::Script::String.new(self.value + other_str, self.type)
|
40
|
-
end
|
41
|
-
|
42
|
-
# @see Node#to_s
|
43
|
-
def to_s(opts = {})
|
44
|
-
if self.type == :identifier
|
45
|
-
return %q{""} if context == :equals && self.value.size == 0
|
46
|
-
return self.value.gsub("\n", " ")
|
47
|
-
end
|
48
|
-
|
49
|
-
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|
50
|
-
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
|
51
|
-
return "\"#{value}\"" unless value.include?('"')
|
52
|
-
return "'#{value}'" unless value.include?("'")
|
53
|
-
"\"#{value.gsub('"', "\\\"")}\"" #'
|
54
|
-
end
|
55
|
-
|
56
|
-
# @see Node#to_sass
|
57
|
-
def to_sass(opts = {})
|
58
|
-
if self.type == :identifier && context == :equals &&
|
59
|
-
self.value !~ Sass::SCSS::RX::URI &&
|
60
|
-
Sass::SCSS::RX.escape_ident(self.value).include?(?\\)
|
61
|
-
return "unquote(#{Sass::Script::String.new(self.value, :string).to_sass})"
|
62
|
-
else
|
63
|
-
return to_s
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,93 +0,0 @@
|
|
1
|
-
module Sass::Script
|
2
|
-
# A SassScript object representing `#{}` interpolation within a string.
|
3
|
-
#
|
4
|
-
# @see Interpolation
|
5
|
-
class StringInterpolation < Node
|
6
|
-
# Interpolation in a string is of the form `"before #{mid} after"`,
|
7
|
-
# where `before` and `after` may include more interpolation.
|
8
|
-
#
|
9
|
-
# @param before [Node] The string before the interpolation
|
10
|
-
# @param mid [Node] The SassScript within the interpolation
|
11
|
-
# @param after [Node] The string after the interpolation
|
12
|
-
def initialize(before, mid, after)
|
13
|
-
@before = before
|
14
|
-
@mid = mid
|
15
|
-
@after = after
|
16
|
-
end
|
17
|
-
|
18
|
-
# @return [String] A human-readable s-expression representation of the interpolation
|
19
|
-
def inspect
|
20
|
-
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
|
21
|
-
end
|
22
|
-
|
23
|
-
# @see Node#to_sass
|
24
|
-
def to_sass(opts = {})
|
25
|
-
# We can get rid of all of this when we remove the deprecated :equals context
|
26
|
-
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
|
27
|
-
after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
|
28
|
-
unquote = before_unquote || after_unquote ||
|
29
|
-
(before_quote_char && !after_quote_char && !after_str.empty?) ||
|
30
|
-
(!before_quote_char && after_quote_char && !before_str.empty?)
|
31
|
-
quote_char =
|
32
|
-
if before_quote_char && after_quote_char && before_quote_char != after_quote_char
|
33
|
-
before_str.gsub!("\\'", "'")
|
34
|
-
before_str.gsub!('"', "\\\"")
|
35
|
-
after_str.gsub!("\\'", "'")
|
36
|
-
after_str.gsub!('"', "\\\"")
|
37
|
-
'"'
|
38
|
-
else
|
39
|
-
before_quote_char || after_quote_char
|
40
|
-
end
|
41
|
-
|
42
|
-
res = ""
|
43
|
-
res << 'unquote(' if unquote
|
44
|
-
res << quote_char if quote_char
|
45
|
-
res << before_str
|
46
|
-
res << '#{' << @mid.to_sass(opts) << '}'
|
47
|
-
res << after_str
|
48
|
-
res << quote_char if quote_char
|
49
|
-
res << ')' if unquote
|
50
|
-
res
|
51
|
-
end
|
52
|
-
|
53
|
-
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
|
54
|
-
#
|
55
|
-
# @return [Array<Node>]
|
56
|
-
# @see #initialize
|
57
|
-
# @see Node#children
|
58
|
-
def children
|
59
|
-
[@before, @mid, @after].compact
|
60
|
-
end
|
61
|
-
|
62
|
-
protected
|
63
|
-
|
64
|
-
# Evaluates the interpolation.
|
65
|
-
#
|
66
|
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
67
|
-
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
|
68
|
-
def _perform(environment)
|
69
|
-
res = ""
|
70
|
-
before = @before.perform(environment)
|
71
|
-
res << before.value
|
72
|
-
mid = @mid.perform(environment)
|
73
|
-
res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
|
74
|
-
res << @after.perform(environment).value
|
75
|
-
opts(Sass::Script::String.new(res, before.type))
|
76
|
-
end
|
77
|
-
|
78
|
-
private
|
79
|
-
|
80
|
-
def parse_str(str)
|
81
|
-
case str
|
82
|
-
when /^unquote\((["'])(.*)\1\)$/
|
83
|
-
return true, $1, $2
|
84
|
-
when '""'
|
85
|
-
return false, nil, ""
|
86
|
-
when /^(["'])(.*)\1$/
|
87
|
-
return false, $1, $2
|
88
|
-
else
|
89
|
-
return false, nil, str
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module Sass::Script
|
2
|
-
# A SassScript parse node representing a unary operation,
|
3
|
-
# such as `-$b` or `not true`.
|
4
|
-
#
|
5
|
-
# Currently only `-`, `/`, and `not` are unary operators.
|
6
|
-
class UnaryOperation < Node
|
7
|
-
# @param operand [Script::Node] The parse-tree node
|
8
|
-
# for the object of the operator
|
9
|
-
# @param operator [Symbol] The operator to perform
|
10
|
-
def initialize(operand, operator)
|
11
|
-
@operand = operand
|
12
|
-
@operator = operator
|
13
|
-
super()
|
14
|
-
end
|
15
|
-
|
16
|
-
# @return [String] A human-readable s-expression representation of the operation
|
17
|
-
def inspect
|
18
|
-
"(#{@operator.inspect} #{@operand.inspect})"
|
19
|
-
end
|
20
|
-
|
21
|
-
# @see Node#to_sass
|
22
|
-
def to_sass(opts = {})
|
23
|
-
operand = @operand.to_sass(opts)
|
24
|
-
if @operand.is_a?(Operation) ||
|
25
|
-
(@operator == :minus &&
|
26
|
-
(operand =~ Sass::SCSS::RX::IDENT) == 0)
|
27
|
-
operand = "(#{@operand.to_sass(opts)})"
|
28
|
-
end
|
29
|
-
op = Lexer::OPERATORS_REVERSE[@operator]
|
30
|
-
op + (op =~ /[a-z]/ ? " " : "") + operand
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns the operand of the operation.
|
34
|
-
#
|
35
|
-
# @return [Array<Node>]
|
36
|
-
# @see Node#children
|
37
|
-
def children
|
38
|
-
[@operand]
|
39
|
-
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
# Evaluates the operation.
|
44
|
-
#
|
45
|
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
46
|
-
# @return [Literal] The SassScript object that is the value of the operation
|
47
|
-
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
|
48
|
-
def _perform(environment)
|
49
|
-
operator = "unary_#{@operator}"
|
50
|
-
literal = @operand.perform(environment)
|
51
|
-
literal.send(operator)
|
52
|
-
rescue NoMethodError => e
|
53
|
-
raise e unless e.name.to_s == operator.to_s
|
54
|
-
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/sass/script/variable.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
module Sass
|
2
|
-
module Script
|
3
|
-
# A SassScript parse node representing a variable.
|
4
|
-
class Variable < Node
|
5
|
-
# The name of the variable.
|
6
|
-
#
|
7
|
-
# @return [String]
|
8
|
-
attr_reader :name
|
9
|
-
|
10
|
-
# @param name [String] See \{#name}
|
11
|
-
def initialize(name)
|
12
|
-
@name = name
|
13
|
-
super()
|
14
|
-
end
|
15
|
-
|
16
|
-
# @return [String] A string representation of the variable
|
17
|
-
def inspect(opts = {})
|
18
|
-
return "!important" if name == "important"
|
19
|
-
"$#{dasherize(name, opts)}"
|
20
|
-
end
|
21
|
-
alias_method :to_sass, :inspect
|
22
|
-
|
23
|
-
# Returns an empty array.
|
24
|
-
#
|
25
|
-
# @return [Array<Node>] empty
|
26
|
-
# @see Node#children
|
27
|
-
def children
|
28
|
-
[]
|
29
|
-
end
|
30
|
-
|
31
|
-
protected
|
32
|
-
|
33
|
-
# Evaluates the variable.
|
34
|
-
#
|
35
|
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
36
|
-
# @return [Literal] The SassScript object that is the value of the variable
|
37
|
-
# @raise [Sass::SyntaxError] if the variable is undefined
|
38
|
-
def _perform(environment)
|
39
|
-
raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
|
40
|
-
if val.is_a?(Number)
|
41
|
-
val = val.dup
|
42
|
-
val.original = nil
|
43
|
-
end
|
44
|
-
return val
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/sass/scss.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'sass/scss/rx'
|
2
|
-
require 'sass/scss/script_lexer'
|
3
|
-
require 'sass/scss/script_parser'
|
4
|
-
require 'sass/scss/parser'
|
5
|
-
require 'sass/scss/sass_parser'
|
6
|
-
require 'sass/scss/static_parser'
|
7
|
-
require 'sass/scss/css_parser'
|
8
|
-
|
9
|
-
module Sass
|
10
|
-
# SCSS is the CSS syntax for Sass.
|
11
|
-
# It parses into the same syntax tree as Sass,
|
12
|
-
# and generates the same sort of output CSS.
|
13
|
-
#
|
14
|
-
# This module contains code for the parsing of SCSS.
|
15
|
-
# The evaluation is handled by the broader {Sass} module.
|
16
|
-
module SCSS; end
|
17
|
-
end
|
data/lib/sass/scss/css_parser.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'sass/script/css_parser'
|
2
|
-
|
3
|
-
module Sass
|
4
|
-
module SCSS
|
5
|
-
# This is a subclass of {Parser} which only parses plain CSS.
|
6
|
-
# It doesn't support any Sass extensions, such as interpolation,
|
7
|
-
# parent references, nested selectors, and so forth.
|
8
|
-
# It does support all the same CSS hacks as the SCSS parser, though.
|
9
|
-
class CssParser < StaticParser
|
10
|
-
# Parse a selector, and return its value as a string.
|
11
|
-
#
|
12
|
-
# @return [String, nil] The parsed selector, or nil if no selector was parsed
|
13
|
-
# @raise [Sass::SyntaxError] if there's a syntax error in the selector
|
14
|
-
def parse_selector_string
|
15
|
-
init_scanner!
|
16
|
-
str {return unless selector}
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
def parent_selector; nil; end
|
22
|
-
def interpolation; nil; end
|
23
|
-
def interp_string; tok(STRING); end
|
24
|
-
def interp_ident(ident = IDENT); tok(ident); end
|
25
|
-
def use_css_import?; true; end
|
26
|
-
|
27
|
-
def block_child(context)
|
28
|
-
case context
|
29
|
-
when :ruleset
|
30
|
-
declaration
|
31
|
-
when :stylesheet
|
32
|
-
directive || ruleset
|
33
|
-
when :directive
|
34
|
-
directive || declaration_or_ruleset
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def nested_properties!(node, space)
|
39
|
-
expected('expression (e.g. 1px, bold)');
|
40
|
-
end
|
41
|
-
|
42
|
-
@sass_script_parser = Class.new(Sass::Script::CssParser)
|
43
|
-
@sass_script_parser.send(:include, ScriptParser)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
data/lib/sass/scss/parser.rb
DELETED
@@ -1,855 +0,0 @@
|
|
1
|
-
require 'strscan'
|
2
|
-
require 'set'
|
3
|
-
|
4
|
-
module Sass
|
5
|
-
module SCSS
|
6
|
-
# The parser for SCSS.
|
7
|
-
# It parses a string of code into a tree of {Sass::Tree::Node}s.
|
8
|
-
class Parser
|
9
|
-
# @param str [String, StringScanner] The source document to parse.
|
10
|
-
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
|
11
|
-
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
|
12
|
-
# @param line [Fixnum] The line on which the source string appeared,
|
13
|
-
# if it's part of another document
|
14
|
-
def initialize(str, line = 1)
|
15
|
-
@template = str
|
16
|
-
@line = line
|
17
|
-
@strs = []
|
18
|
-
end
|
19
|
-
|
20
|
-
# Parses an SCSS document.
|
21
|
-
#
|
22
|
-
# @return [Sass::Tree::RootNode] The root node of the document tree
|
23
|
-
# @raise [Sass::SyntaxError] if there's a syntax error in the document
|
24
|
-
def parse
|
25
|
-
init_scanner!
|
26
|
-
root = stylesheet
|
27
|
-
expected("selector or at-rule") unless @scanner.eos?
|
28
|
-
root
|
29
|
-
end
|
30
|
-
|
31
|
-
# Parses an identifier with interpolation.
|
32
|
-
# Note that this won't assert that the identifier takes up the entire input string;
|
33
|
-
# it's meant to be used with `StringScanner`s as part of other parsers.
|
34
|
-
#
|
35
|
-
# @return [Array<String, Sass::Script::Node>, nil]
|
36
|
-
# The interpolated identifier, or nil if none could be parsed
|
37
|
-
def parse_interp_ident
|
38
|
-
init_scanner!
|
39
|
-
interp_ident
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
include Sass::SCSS::RX
|
45
|
-
|
46
|
-
def init_scanner!
|
47
|
-
@scanner =
|
48
|
-
if @template.is_a?(StringScanner)
|
49
|
-
@template
|
50
|
-
else
|
51
|
-
StringScanner.new(@template.gsub("\r", ""))
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def stylesheet
|
56
|
-
node = node(Sass::Tree::RootNode.new(@scanner.string))
|
57
|
-
block_contents(node, :stylesheet) {s(node)}
|
58
|
-
end
|
59
|
-
|
60
|
-
def s(node)
|
61
|
-
while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
|
62
|
-
next unless c
|
63
|
-
process_comment c, node
|
64
|
-
c = nil
|
65
|
-
end
|
66
|
-
true
|
67
|
-
end
|
68
|
-
|
69
|
-
def ss
|
70
|
-
nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
|
71
|
-
true
|
72
|
-
end
|
73
|
-
|
74
|
-
def ss_comments(node)
|
75
|
-
while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
|
76
|
-
next unless c
|
77
|
-
process_comment c, node
|
78
|
-
c = nil
|
79
|
-
end
|
80
|
-
|
81
|
-
true
|
82
|
-
end
|
83
|
-
|
84
|
-
def whitespace
|
85
|
-
return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
|
86
|
-
ss
|
87
|
-
end
|
88
|
-
|
89
|
-
def process_comment(text, node)
|
90
|
-
single_line = text =~ /^\/\//
|
91
|
-
pre_str = single_line ? "" : @scanner.
|
92
|
-
string[0...@scanner.pos].
|
93
|
-
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
|
94
|
-
reverse.gsub(/[^\s]/, ' ')
|
95
|
-
text = text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */' if single_line
|
96
|
-
comment = Sass::Tree::CommentNode.new(pre_str + text, single_line)
|
97
|
-
comment.line = @line - text.count("\n")
|
98
|
-
node << comment
|
99
|
-
end
|
100
|
-
|
101
|
-
DIRECTIVES = Set[:mixin, :include, :debug, :warn, :for, :while, :if, :else,
|
102
|
-
:extend, :import, :media, :charset]
|
103
|
-
|
104
|
-
def directive
|
105
|
-
return unless tok(/@/)
|
106
|
-
name = tok!(IDENT)
|
107
|
-
ss
|
108
|
-
|
109
|
-
if dir = special_directive(name)
|
110
|
-
return dir
|
111
|
-
end
|
112
|
-
|
113
|
-
# Most at-rules take expressions (e.g. @import),
|
114
|
-
# but some (e.g. @page) take selector-like arguments
|
115
|
-
val = str {break unless expr}
|
116
|
-
val ||= CssParser.new(@scanner, @line).parse_selector_string
|
117
|
-
node = node(Sass::Tree::DirectiveNode.new("@#{name} #{val}".strip))
|
118
|
-
|
119
|
-
if tok(/\{/)
|
120
|
-
node.has_children = true
|
121
|
-
block_contents(node, :directive)
|
122
|
-
tok!(/\}/)
|
123
|
-
end
|
124
|
-
|
125
|
-
node
|
126
|
-
end
|
127
|
-
|
128
|
-
def special_directive(name)
|
129
|
-
sym = name.gsub('-', '_').to_sym
|
130
|
-
DIRECTIVES.include?(sym) && send("#{sym}_directive")
|
131
|
-
end
|
132
|
-
|
133
|
-
def mixin_directive
|
134
|
-
name = tok! IDENT
|
135
|
-
args = sass_script(:parse_mixin_definition_arglist)
|
136
|
-
ss
|
137
|
-
block(node(Sass::Tree::MixinDefNode.new(name, args)), :directive)
|
138
|
-
end
|
139
|
-
|
140
|
-
def include_directive
|
141
|
-
name = tok! IDENT
|
142
|
-
args = sass_script(:parse_mixin_include_arglist)
|
143
|
-
ss
|
144
|
-
node(Sass::Tree::MixinNode.new(name, args))
|
145
|
-
end
|
146
|
-
|
147
|
-
def debug_directive
|
148
|
-
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
|
149
|
-
end
|
150
|
-
|
151
|
-
def warn_directive
|
152
|
-
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
|
153
|
-
end
|
154
|
-
|
155
|
-
def for_directive
|
156
|
-
tok!(/\$/)
|
157
|
-
var = tok! IDENT
|
158
|
-
ss
|
159
|
-
|
160
|
-
tok!(/from/)
|
161
|
-
from = sass_script(:parse_until, Set["to", "through"])
|
162
|
-
ss
|
163
|
-
|
164
|
-
@expected = '"to" or "through"'
|
165
|
-
exclusive = (tok(/to/) || tok!(/through/)) == 'to'
|
166
|
-
to = sass_script(:parse)
|
167
|
-
ss
|
168
|
-
|
169
|
-
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
|
170
|
-
end
|
171
|
-
|
172
|
-
def while_directive
|
173
|
-
expr = sass_script(:parse)
|
174
|
-
ss
|
175
|
-
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
|
176
|
-
end
|
177
|
-
|
178
|
-
def if_directive
|
179
|
-
expr = sass_script(:parse)
|
180
|
-
ss
|
181
|
-
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
|
182
|
-
pos = @scanner.pos
|
183
|
-
line = @line
|
184
|
-
ss
|
185
|
-
|
186
|
-
else_block(node) ||
|
187
|
-
begin
|
188
|
-
# Backtrack in case there are any comments we want to parse
|
189
|
-
@scanner.pos = pos
|
190
|
-
@line = line
|
191
|
-
node
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def else_block(node)
|
196
|
-
return unless tok(/@else/)
|
197
|
-
ss
|
198
|
-
else_node = block(
|
199
|
-
Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
|
200
|
-
:directive)
|
201
|
-
node.add_else(else_node)
|
202
|
-
pos = @scanner.pos
|
203
|
-
line = @line
|
204
|
-
ss
|
205
|
-
|
206
|
-
else_block(node) ||
|
207
|
-
begin
|
208
|
-
# Backtrack in case there are any comments we want to parse
|
209
|
-
@scanner.pos = pos
|
210
|
-
@line = line
|
211
|
-
node
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
def else_directive
|
216
|
-
raise Sass::SyntaxError.new(
|
217
|
-
"Invalid CSS: @else must come after @if", :line => @line)
|
218
|
-
end
|
219
|
-
|
220
|
-
def extend_directive
|
221
|
-
node(Sass::Tree::ExtendNode.new(expr!(:selector)))
|
222
|
-
end
|
223
|
-
|
224
|
-
def import_directive
|
225
|
-
values = []
|
226
|
-
|
227
|
-
loop do
|
228
|
-
values << expr!(:import_arg)
|
229
|
-
break if use_css_import? || !tok(/,\s*/)
|
230
|
-
end
|
231
|
-
|
232
|
-
return values
|
233
|
-
end
|
234
|
-
|
235
|
-
def import_arg
|
236
|
-
return unless arg = tok(STRING) || (uri = tok!(URI))
|
237
|
-
path = @scanner[1] || @scanner[2] || @scanner[3]
|
238
|
-
ss
|
239
|
-
|
240
|
-
media = str {media_query_list}.strip
|
241
|
-
|
242
|
-
if uri || path =~ /^http:\/\// || !media.strip.empty? || use_css_import?
|
243
|
-
return node(Sass::Tree::DirectiveNode.new("@import #{arg} #{media}".strip))
|
244
|
-
end
|
245
|
-
|
246
|
-
node(Sass::Tree::ImportNode.new(path.strip))
|
247
|
-
end
|
248
|
-
|
249
|
-
def use_css_import?; false; end
|
250
|
-
|
251
|
-
def media_directive
|
252
|
-
val = str {media_query_list}.strip
|
253
|
-
block(node(Sass::Tree::DirectiveNode.new("@media #{val}")), :directive)
|
254
|
-
end
|
255
|
-
|
256
|
-
# http://www.w3.org/TR/css3-mediaqueries/#syntax
|
257
|
-
def media_query_list
|
258
|
-
return unless media_query
|
259
|
-
|
260
|
-
ss
|
261
|
-
while tok(/,/)
|
262
|
-
ss; expr!(:media_query); ss
|
263
|
-
end
|
264
|
-
|
265
|
-
true
|
266
|
-
end
|
267
|
-
|
268
|
-
def media_query
|
269
|
-
if tok(/only|not/i)
|
270
|
-
ss
|
271
|
-
@expected = "media type (e.g. print, screen)"
|
272
|
-
tok!(IDENT)
|
273
|
-
ss
|
274
|
-
elsif !tok(IDENT) && !media_expr
|
275
|
-
return
|
276
|
-
end
|
277
|
-
|
278
|
-
ss
|
279
|
-
while tok(/and/i)
|
280
|
-
ss; expr!(:media_expr); ss
|
281
|
-
end
|
282
|
-
|
283
|
-
true
|
284
|
-
end
|
285
|
-
|
286
|
-
def media_expr
|
287
|
-
return unless tok(/\(/)
|
288
|
-
ss
|
289
|
-
@expected = "media feature (e.g. min-device-width, color)"
|
290
|
-
tok!(IDENT)
|
291
|
-
ss
|
292
|
-
|
293
|
-
if tok(/:/)
|
294
|
-
ss; expr!(:expr)
|
295
|
-
end
|
296
|
-
tok!(/\)/)
|
297
|
-
ss
|
298
|
-
|
299
|
-
true
|
300
|
-
end
|
301
|
-
|
302
|
-
def charset_directive
|
303
|
-
tok! STRING
|
304
|
-
name = @scanner[1] || @scanner[2]
|
305
|
-
ss
|
306
|
-
node(Sass::Tree::CharsetNode.new(name))
|
307
|
-
end
|
308
|
-
|
309
|
-
def variable
|
310
|
-
return unless tok(/\$/)
|
311
|
-
name = tok!(IDENT)
|
312
|
-
ss; tok!(/:/); ss
|
313
|
-
|
314
|
-
expr = sass_script(:parse)
|
315
|
-
guarded = tok(DEFAULT)
|
316
|
-
node(Sass::Tree::VariableNode.new(name, expr, guarded))
|
317
|
-
end
|
318
|
-
|
319
|
-
def operator
|
320
|
-
# Many of these operators (all except / and ,)
|
321
|
-
# are disallowed by the CSS spec,
|
322
|
-
# but they're included here for compatibility
|
323
|
-
# with some proprietary MS properties
|
324
|
-
str {ss if tok(/[\/,:.=]/)}
|
325
|
-
end
|
326
|
-
|
327
|
-
def unary_operator
|
328
|
-
tok(/[+-]/)
|
329
|
-
end
|
330
|
-
|
331
|
-
def ruleset
|
332
|
-
return unless rules = selector_sequence
|
333
|
-
block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
|
334
|
-
end
|
335
|
-
|
336
|
-
def block(node, context)
|
337
|
-
node.has_children = true
|
338
|
-
tok!(/\{/)
|
339
|
-
block_contents(node, context)
|
340
|
-
tok!(/\}/)
|
341
|
-
node
|
342
|
-
end
|
343
|
-
|
344
|
-
# A block may contain declarations and/or rulesets
|
345
|
-
def block_contents(node, context)
|
346
|
-
block_given? ? yield : ss_comments(node)
|
347
|
-
node << (child = block_child(context))
|
348
|
-
while tok(/;/) || has_children?(child)
|
349
|
-
block_given? ? yield : ss_comments(node)
|
350
|
-
node << (child = block_child(context))
|
351
|
-
end
|
352
|
-
node
|
353
|
-
end
|
354
|
-
|
355
|
-
def block_child(context)
|
356
|
-
return variable || directive || ruleset if context == :stylesheet
|
357
|
-
variable || directive || declaration_or_ruleset
|
358
|
-
end
|
359
|
-
|
360
|
-
def has_children?(child_or_array)
|
361
|
-
return false unless child_or_array
|
362
|
-
return child_or_array.last.has_children if child_or_array.is_a?(Array)
|
363
|
-
return child_or_array.has_children
|
364
|
-
end
|
365
|
-
|
366
|
-
# This is a nasty hack, and the only place in the parser
|
367
|
-
# that requires backtracking.
|
368
|
-
# The reason is that we can't figure out if certain strings
|
369
|
-
# are declarations or rulesets with fixed finite lookahead.
|
370
|
-
# For example, "foo:bar baz baz baz..." could be either a property
|
371
|
-
# or a selector.
|
372
|
-
#
|
373
|
-
# To handle this, we simply check if it works as a property
|
374
|
-
# (which is the most common case)
|
375
|
-
# and, if it doesn't, try it as a ruleset.
|
376
|
-
#
|
377
|
-
# We could eke some more efficiency out of this
|
378
|
-
# by handling some easy cases (first token isn't an identifier,
|
379
|
-
# no colon after the identifier, whitespace after the colon),
|
380
|
-
# but I'm not sure the gains would be worth the added complexity.
|
381
|
-
def declaration_or_ruleset
|
382
|
-
pos = @scanner.pos
|
383
|
-
line = @line
|
384
|
-
old_use_property_exception, @use_property_exception =
|
385
|
-
@use_property_exception, false
|
386
|
-
begin
|
387
|
-
decl = declaration
|
388
|
-
unless decl && decl.has_children
|
389
|
-
# We want an exception if it's not there,
|
390
|
-
# but we don't want to consume if it is
|
391
|
-
tok!(/[;}]/) unless tok?(/[;}]/)
|
392
|
-
end
|
393
|
-
return decl
|
394
|
-
rescue Sass::SyntaxError => decl_err
|
395
|
-
end
|
396
|
-
|
397
|
-
@line = line
|
398
|
-
@scanner.pos = pos
|
399
|
-
|
400
|
-
begin
|
401
|
-
return ruleset
|
402
|
-
rescue Sass::SyntaxError => ruleset_err
|
403
|
-
raise @use_property_exception ? decl_err : ruleset_err
|
404
|
-
end
|
405
|
-
ensure
|
406
|
-
@use_property_exception = old_use_property_exception
|
407
|
-
end
|
408
|
-
|
409
|
-
def selector_sequence
|
410
|
-
if sel = tok(STATIC_SELECTOR)
|
411
|
-
return [sel]
|
412
|
-
end
|
413
|
-
|
414
|
-
rules = []
|
415
|
-
return unless v = selector
|
416
|
-
rules.concat v
|
417
|
-
|
418
|
-
while tok(/,/)
|
419
|
-
rules << ',' << str {ss}
|
420
|
-
rules.concat expr!(:selector)
|
421
|
-
end
|
422
|
-
rules
|
423
|
-
end
|
424
|
-
|
425
|
-
def selector
|
426
|
-
return unless sel = _selector
|
427
|
-
sel.to_a
|
428
|
-
end
|
429
|
-
|
430
|
-
def selector_comma_sequence
|
431
|
-
return unless sel = _selector
|
432
|
-
selectors = [sel]
|
433
|
-
while tok(/,/)
|
434
|
-
ws = str{ss}
|
435
|
-
selectors << expr!(:_selector)
|
436
|
-
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
|
437
|
-
end
|
438
|
-
Selector::CommaSequence.new(selectors)
|
439
|
-
end
|
440
|
-
|
441
|
-
def _selector
|
442
|
-
# The combinator here allows the "> E" hack
|
443
|
-
return unless val = combinator || simple_selector_sequence
|
444
|
-
nl = str{ss}.include?("\n")
|
445
|
-
res = []
|
446
|
-
res << val
|
447
|
-
res << "\n" if nl
|
448
|
-
|
449
|
-
while val = combinator || simple_selector_sequence
|
450
|
-
res << val
|
451
|
-
res << "\n" if str{ss}.include?("\n")
|
452
|
-
end
|
453
|
-
Selector::Sequence.new(res.compact)
|
454
|
-
end
|
455
|
-
|
456
|
-
def combinator
|
457
|
-
tok(PLUS) || tok(GREATER) || tok(TILDE)
|
458
|
-
end
|
459
|
-
|
460
|
-
def simple_selector_sequence
|
461
|
-
# This allows for stuff like http://www.w3.org/TR/css3-animations/#keyframes-
|
462
|
-
return expr unless e = element_name || id_selector || class_selector ||
|
463
|
-
attrib || negation || pseudo || parent_selector || interpolation_selector
|
464
|
-
res = [e]
|
465
|
-
|
466
|
-
# The tok(/\*/) allows the "E*" hack
|
467
|
-
while v = element_name || id_selector || class_selector ||
|
468
|
-
attrib || negation || pseudo || interpolation_selector ||
|
469
|
-
(tok(/\*/) && Selector::Universal.new(nil))
|
470
|
-
res << v
|
471
|
-
end
|
472
|
-
|
473
|
-
if tok?(/&/)
|
474
|
-
begin
|
475
|
-
expected('"{"')
|
476
|
-
rescue Sass::SyntaxError => e
|
477
|
-
e.message << "\n\n" << <<MESSAGE
|
478
|
-
In Sass 3, the parent selector & can only be used where element names are valid,
|
479
|
-
since it could potentially be replaced by an element name.
|
480
|
-
MESSAGE
|
481
|
-
raise e
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
Selector::SimpleSequence.new(res)
|
486
|
-
end
|
487
|
-
|
488
|
-
def parent_selector
|
489
|
-
return unless tok(/&/)
|
490
|
-
Selector::Parent.new
|
491
|
-
end
|
492
|
-
|
493
|
-
def class_selector
|
494
|
-
return unless tok(/\./)
|
495
|
-
@expected = "class name"
|
496
|
-
Selector::Class.new(merge(expr!(:interp_ident)))
|
497
|
-
end
|
498
|
-
|
499
|
-
def id_selector
|
500
|
-
return unless tok(/#(?!\{)/)
|
501
|
-
@expected = "id name"
|
502
|
-
Selector::Id.new(merge(expr!(:interp_name)))
|
503
|
-
end
|
504
|
-
|
505
|
-
def element_name
|
506
|
-
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
|
507
|
-
if tok(/\|/)
|
508
|
-
@expected = "element name or *"
|
509
|
-
ns = name
|
510
|
-
name = interp_ident || tok!(/\*/)
|
511
|
-
end
|
512
|
-
|
513
|
-
if name == '*'
|
514
|
-
Selector::Universal.new(merge(ns))
|
515
|
-
else
|
516
|
-
Selector::Element.new(merge(name), merge(ns))
|
517
|
-
end
|
518
|
-
end
|
519
|
-
|
520
|
-
def interpolation_selector
|
521
|
-
return unless script = interpolation
|
522
|
-
Selector::Interpolation.new(script)
|
523
|
-
end
|
524
|
-
|
525
|
-
def attrib
|
526
|
-
return unless tok(/\[/)
|
527
|
-
ss
|
528
|
-
ns, name = attrib_name!
|
529
|
-
ss
|
530
|
-
|
531
|
-
if op = tok(/=/) ||
|
532
|
-
tok(INCLUDES) ||
|
533
|
-
tok(DASHMATCH) ||
|
534
|
-
tok(PREFIXMATCH) ||
|
535
|
-
tok(SUFFIXMATCH) ||
|
536
|
-
tok(SUBSTRINGMATCH)
|
537
|
-
@expected = "identifier or string"
|
538
|
-
ss
|
539
|
-
if val = tok(IDENT)
|
540
|
-
val = [val]
|
541
|
-
else
|
542
|
-
val = expr!(:interp_string)
|
543
|
-
end
|
544
|
-
ss
|
545
|
-
end
|
546
|
-
tok(/\]/)
|
547
|
-
|
548
|
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val))
|
549
|
-
end
|
550
|
-
|
551
|
-
def attrib_name!
|
552
|
-
if name_or_ns = interp_ident
|
553
|
-
# E, E|E
|
554
|
-
if tok(/\|(?!=)/)
|
555
|
-
ns = name_or_ns
|
556
|
-
name = interp_ident
|
557
|
-
else
|
558
|
-
name = name_or_ns
|
559
|
-
end
|
560
|
-
else
|
561
|
-
# *|E or |E
|
562
|
-
ns = [tok(/\*/) || ""]
|
563
|
-
tok!(/\|/)
|
564
|
-
name = expr!(:interp_ident)
|
565
|
-
end
|
566
|
-
return ns, name
|
567
|
-
end
|
568
|
-
|
569
|
-
def pseudo
|
570
|
-
return unless s = tok(/::?/)
|
571
|
-
@expected = "pseudoclass or pseudoelement"
|
572
|
-
name = expr!(:interp_ident)
|
573
|
-
if tok(/\(/)
|
574
|
-
ss
|
575
|
-
arg = expr!(:pseudo_expr)
|
576
|
-
tok!(/\)/)
|
577
|
-
end
|
578
|
-
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
|
579
|
-
end
|
580
|
-
|
581
|
-
def pseudo_expr
|
582
|
-
return unless e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
|
583
|
-
interp_string || tok(IDENT) || interpolation
|
584
|
-
res = [e, str{ss}]
|
585
|
-
while e = tok(PLUS) || tok(/-/) || tok(NUMBER) ||
|
586
|
-
interp_string || tok(IDENT) || interpolation
|
587
|
-
res << e << str{ss}
|
588
|
-
end
|
589
|
-
res
|
590
|
-
end
|
591
|
-
|
592
|
-
def negation
|
593
|
-
return unless name = tok(NOT) || tok(MOZ_ANY)
|
594
|
-
ss
|
595
|
-
@expected = "selector"
|
596
|
-
sel = selector_comma_sequence
|
597
|
-
tok!(/\)/)
|
598
|
-
Selector::SelectorPseudoClass.new(name[1...-1], sel)
|
599
|
-
end
|
600
|
-
|
601
|
-
def declaration
|
602
|
-
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
|
603
|
-
if s = tok(/[:\*\.]|\#(?!\{)/)
|
604
|
-
@use_property_exception = s !~ /[\.\#]/
|
605
|
-
name = [s, str{ss}, *expr!(:interp_ident)]
|
606
|
-
else
|
607
|
-
return unless name = interp_ident
|
608
|
-
name = [name] if name.is_a?(String)
|
609
|
-
end
|
610
|
-
if comment = tok(COMMENT)
|
611
|
-
name << comment
|
612
|
-
end
|
613
|
-
ss
|
614
|
-
|
615
|
-
tok!(/:/)
|
616
|
-
space, value = value!
|
617
|
-
ss
|
618
|
-
require_block = tok?(/\{/)
|
619
|
-
|
620
|
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
|
621
|
-
|
622
|
-
return node unless require_block
|
623
|
-
nested_properties! node, space
|
624
|
-
end
|
625
|
-
|
626
|
-
def value!
|
627
|
-
space = !str {ss}.empty?
|
628
|
-
@use_property_exception ||= space || !tok?(IDENT)
|
629
|
-
|
630
|
-
return true, Sass::Script::String.new("") if tok?(/\{/)
|
631
|
-
# This is a bit of a dirty trick:
|
632
|
-
# if the value is completely static,
|
633
|
-
# we don't parse it at all, and instead return a plain old string
|
634
|
-
# containing the value.
|
635
|
-
# This results in a dramatic speed increase.
|
636
|
-
if val = tok(STATIC_VALUE)
|
637
|
-
return space, Sass::Script::String.new(val.strip)
|
638
|
-
end
|
639
|
-
return space, sass_script(:parse)
|
640
|
-
end
|
641
|
-
|
642
|
-
def plain_value
|
643
|
-
return unless tok(/:/)
|
644
|
-
space = !str {ss}.empty?
|
645
|
-
@use_property_exception ||= space || !tok?(IDENT)
|
646
|
-
|
647
|
-
expression = expr
|
648
|
-
expression << tok(IMPORTANT) if expression
|
649
|
-
# expression, space, value
|
650
|
-
return expression, space, expression || [""]
|
651
|
-
end
|
652
|
-
|
653
|
-
def nested_properties!(node, space)
|
654
|
-
raise Sass::SyntaxError.new(<<MESSAGE, :line => @line) unless space
|
655
|
-
Invalid CSS: a space is required between a property and its definition
|
656
|
-
when it has other properties nested beneath it.
|
657
|
-
MESSAGE
|
658
|
-
|
659
|
-
@use_property_exception = true
|
660
|
-
@expected = 'expression (e.g. 1px, bold) or "{"'
|
661
|
-
block(node, :property)
|
662
|
-
end
|
663
|
-
|
664
|
-
def expr
|
665
|
-
return unless t = term
|
666
|
-
res = [t, str{ss}]
|
667
|
-
|
668
|
-
while (o = operator) && (t = term)
|
669
|
-
res << o << t << str{ss}
|
670
|
-
end
|
671
|
-
|
672
|
-
res
|
673
|
-
end
|
674
|
-
|
675
|
-
def term
|
676
|
-
unless e = tok(NUMBER) ||
|
677
|
-
tok(URI) ||
|
678
|
-
function ||
|
679
|
-
tok(STRING) ||
|
680
|
-
tok(UNICODERANGE) ||
|
681
|
-
tok(IDENT) ||
|
682
|
-
tok(HEXCOLOR)
|
683
|
-
|
684
|
-
return unless op = unary_operator
|
685
|
-
@expected = "number or function"
|
686
|
-
return [op, tok(NUMBER) || expr!(:function)]
|
687
|
-
end
|
688
|
-
e
|
689
|
-
end
|
690
|
-
|
691
|
-
def function
|
692
|
-
return unless name = tok(FUNCTION)
|
693
|
-
if name == "expression(" || name == "calc("
|
694
|
-
str, _ = Haml::Shared.balance(@scanner, ?(, ?), 1)
|
695
|
-
[name, str]
|
696
|
-
else
|
697
|
-
[name, str{ss}, expr, tok!(/\)/)]
|
698
|
-
end
|
699
|
-
end
|
700
|
-
|
701
|
-
def interpolation
|
702
|
-
return unless tok(INTERP_START)
|
703
|
-
sass_script(:parse_interpolated)
|
704
|
-
end
|
705
|
-
|
706
|
-
def interp_string
|
707
|
-
_interp_string(:double) || _interp_string(:single)
|
708
|
-
end
|
709
|
-
|
710
|
-
def _interp_string(type)
|
711
|
-
return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, false]])
|
712
|
-
res = [start]
|
713
|
-
|
714
|
-
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[[type, true]]
|
715
|
-
# @scanner[2].empty? means we've started an interpolated section
|
716
|
-
while @scanner[2] == '#{'
|
717
|
-
@scanner.pos -= 2 # Don't consume the #{
|
718
|
-
res.last.slice!(-2..-1)
|
719
|
-
res << expr!(:interpolation) << tok(mid_re)
|
720
|
-
end
|
721
|
-
res
|
722
|
-
end
|
723
|
-
|
724
|
-
def interp_ident(start = IDENT)
|
725
|
-
return unless val = tok(start) || interpolation
|
726
|
-
res = [val]
|
727
|
-
while val = tok(NAME) || interpolation
|
728
|
-
res << val
|
729
|
-
end
|
730
|
-
res
|
731
|
-
end
|
732
|
-
|
733
|
-
def interp_name
|
734
|
-
interp_ident NAME
|
735
|
-
end
|
736
|
-
|
737
|
-
def str
|
738
|
-
@strs.push ""
|
739
|
-
yield
|
740
|
-
@strs.last
|
741
|
-
ensure
|
742
|
-
@strs.pop
|
743
|
-
end
|
744
|
-
|
745
|
-
def str?
|
746
|
-
@strs.push ""
|
747
|
-
yield && @strs.last
|
748
|
-
ensure
|
749
|
-
@strs.pop
|
750
|
-
end
|
751
|
-
|
752
|
-
def node(node)
|
753
|
-
node.line = @line
|
754
|
-
node
|
755
|
-
end
|
756
|
-
|
757
|
-
@sass_script_parser = Class.new(Sass::Script::Parser)
|
758
|
-
@sass_script_parser.send(:include, ScriptParser)
|
759
|
-
# @private
|
760
|
-
def self.sass_script_parser; @sass_script_parser; end
|
761
|
-
|
762
|
-
def sass_script(*args)
|
763
|
-
parser = self.class.sass_script_parser.new(@scanner, @line,
|
764
|
-
@scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
|
765
|
-
result = parser.send(*args)
|
766
|
-
@line = parser.line
|
767
|
-
result
|
768
|
-
end
|
769
|
-
|
770
|
-
def merge(arr)
|
771
|
-
arr && Haml::Util.merge_adjacent_strings([arr].flatten)
|
772
|
-
end
|
773
|
-
|
774
|
-
EXPR_NAMES = {
|
775
|
-
:media_query => "media query (e.g. print, screen, print and screen)",
|
776
|
-
:media_expr => "media expression (e.g. (min-device-width: 800px)))",
|
777
|
-
:pseudo_expr => "expression (e.g. fr, 2n+1)",
|
778
|
-
:interp_ident => "identifier",
|
779
|
-
:interp_name => "identifier",
|
780
|
-
:expr => "expression (e.g. 1px, bold)",
|
781
|
-
:selector_comma_sequence => "selector",
|
782
|
-
:simple_selector_sequence => "selector",
|
783
|
-
:import_arg => "file to import (string or url())",
|
784
|
-
}
|
785
|
-
|
786
|
-
TOK_NAMES = Haml::Util.to_hash(
|
787
|
-
Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
|
788
|
-
merge(IDENT => "identifier", /[;}]/ => '";"')
|
789
|
-
|
790
|
-
def tok?(rx)
|
791
|
-
@scanner.match?(rx)
|
792
|
-
end
|
793
|
-
|
794
|
-
def expr!(name)
|
795
|
-
(e = send(name)) && (return e)
|
796
|
-
expected(EXPR_NAMES[name] || name.to_s)
|
797
|
-
end
|
798
|
-
|
799
|
-
def tok!(rx)
|
800
|
-
(t = tok(rx)) && (return t)
|
801
|
-
name = TOK_NAMES[rx]
|
802
|
-
|
803
|
-
unless name
|
804
|
-
# Display basic regexps as plain old strings
|
805
|
-
string = rx.source.gsub(/\\(.)/, '\1')
|
806
|
-
name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
|
807
|
-
end
|
808
|
-
|
809
|
-
expected(name)
|
810
|
-
end
|
811
|
-
|
812
|
-
def expected(name)
|
813
|
-
self.class.expected(@scanner, @expected || name, @line)
|
814
|
-
end
|
815
|
-
|
816
|
-
# @private
|
817
|
-
def self.expected(scanner, expected, line)
|
818
|
-
pos = scanner.pos
|
819
|
-
|
820
|
-
after = scanner.string[0...pos]
|
821
|
-
# Get rid of whitespace between pos and the last token,
|
822
|
-
# but only if there's a newline in there
|
823
|
-
after.gsub!(/\s*\n\s*$/, '')
|
824
|
-
# Also get rid of stuff before the last newline
|
825
|
-
after.gsub!(/.*\n/, '')
|
826
|
-
after = "..." + after[-15..-1] if after.size > 18
|
827
|
-
|
828
|
-
was = scanner.rest.dup
|
829
|
-
# Get rid of whitespace between pos and the next token,
|
830
|
-
# but only if there's a newline in there
|
831
|
-
was.gsub!(/^\s*\n\s*/, '')
|
832
|
-
# Also get rid of stuff after the next newline
|
833
|
-
was.gsub!(/\n.*/, '')
|
834
|
-
was = was[0...15] + "..." if was.size > 18
|
835
|
-
|
836
|
-
raise Sass::SyntaxError.new(
|
837
|
-
"Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
|
838
|
-
:line => line)
|
839
|
-
end
|
840
|
-
|
841
|
-
def tok(rx)
|
842
|
-
res = @scanner.scan(rx)
|
843
|
-
if res
|
844
|
-
@line += res.count("\n")
|
845
|
-
@expected = nil
|
846
|
-
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
|
847
|
-
@strs.each {|s| s << res}
|
848
|
-
end
|
849
|
-
end
|
850
|
-
|
851
|
-
res
|
852
|
-
end
|
853
|
-
end
|
854
|
-
end
|
855
|
-
end
|