haml 2.2.24 → 3.0.0.beta.1
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 +0 -1
- data/README.md +91 -151
- data/REMEMBER +11 -1
- data/Rakefile +73 -55
- data/VERSION +1 -1
- data/VERSION_NAME +1 -1
- data/bin/css2sass +7 -1
- data/bin/sass-convert +7 -0
- data/extra/haml-mode.el +2 -1
- data/lib/haml/buffer.rb +22 -4
- data/lib/haml/engine.rb +5 -1
- data/lib/haml/exec.rb +231 -46
- data/lib/haml/filters.rb +19 -8
- data/lib/haml/helpers.rb +47 -20
- data/lib/haml/helpers/action_view_extensions.rb +2 -4
- data/lib/haml/helpers/action_view_mods.rb +11 -8
- data/lib/haml/helpers/xss_mods.rb +13 -2
- data/lib/haml/html.rb +179 -48
- data/lib/haml/html/erb.rb +141 -0
- data/lib/haml/precompiler.rb +40 -15
- data/lib/haml/railtie.rb +1 -5
- data/lib/haml/root.rb +3 -0
- data/lib/haml/template.rb +4 -14
- data/lib/haml/util.rb +120 -30
- data/lib/haml/version.rb +25 -2
- data/lib/sass.rb +5 -1
- data/lib/sass/callbacks.rb +50 -0
- data/lib/sass/css.rb +40 -191
- data/lib/sass/engine.rb +170 -74
- data/lib/sass/environment.rb +8 -2
- data/lib/sass/error.rb +163 -25
- data/lib/sass/files.rb +31 -28
- data/lib/sass/plugin.rb +268 -87
- data/lib/sass/plugin/rails.rb +9 -4
- data/lib/sass/repl.rb +1 -1
- data/lib/sass/script.rb +31 -29
- data/lib/sass/script/bool.rb +1 -0
- data/lib/sass/script/color.rb +290 -23
- data/lib/sass/script/css_lexer.rb +22 -0
- data/lib/sass/script/css_parser.rb +28 -0
- data/lib/sass/script/funcall.rb +22 -3
- data/lib/sass/script/functions.rb +523 -33
- data/lib/sass/script/interpolation.rb +42 -0
- data/lib/sass/script/lexer.rb +169 -52
- data/lib/sass/script/literal.rb +58 -9
- data/lib/sass/script/node.rb +79 -1
- data/lib/sass/script/number.rb +20 -5
- data/lib/sass/script/operation.rb +49 -3
- data/lib/sass/script/parser.rb +162 -28
- data/lib/sass/script/string.rb +50 -2
- data/lib/sass/script/unary_operation.rb +25 -2
- data/lib/sass/script/variable.rb +21 -4
- data/lib/sass/scss.rb +14 -0
- data/lib/sass/scss/css_parser.rb +39 -0
- data/lib/sass/scss/parser.rb +683 -0
- data/lib/sass/scss/rx.rb +112 -0
- data/lib/sass/scss/script_lexer.rb +13 -0
- data/lib/sass/scss/script_parser.rb +25 -0
- data/lib/sass/tree/comment_node.rb +69 -27
- data/lib/sass/tree/debug_node.rb +7 -2
- data/lib/sass/tree/directive_node.rb +41 -35
- data/lib/sass/tree/for_node.rb +6 -0
- data/lib/sass/tree/if_node.rb +13 -1
- data/lib/sass/tree/import_node.rb +52 -27
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +41 -6
- data/lib/sass/tree/node.rb +197 -70
- data/lib/sass/tree/prop_node.rb +152 -57
- data/lib/sass/tree/root_node.rb +118 -0
- data/lib/sass/tree/rule_node.rb +193 -96
- data/lib/sass/tree/variable_node.rb +9 -5
- data/lib/sass/tree/while_node.rb +4 -0
- data/test/benchmark.rb +5 -5
- data/test/haml/engine_test.rb +147 -10
- data/test/haml/{rhtml/_av_partial_1.rhtml → erb/_av_partial_1.erb} +1 -1
- data/test/haml/{rhtml/_av_partial_2.rhtml → erb/_av_partial_2.erb} +1 -1
- data/test/haml/{rhtml/action_view.rhtml → erb/action_view.erb} +1 -1
- data/test/haml/{rhtml/standard.rhtml → erb/standard.erb} +0 -0
- data/test/haml/helper_test.rb +91 -24
- data/test/haml/html2haml/erb_tests.rb +410 -0
- data/test/haml/html2haml_test.rb +210 -66
- data/test/haml/results/filters.xhtml +1 -1
- data/test/haml/results/just_stuff.xhtml +2 -0
- data/test/haml/spec_test.rb +44 -0
- data/test/haml/template_test.rb +22 -2
- data/test/haml/templates/helpers.haml +0 -13
- data/test/haml/templates/just_stuff.haml +2 -0
- data/test/haml/util_test.rb +48 -0
- data/test/sass/callbacks_test.rb +61 -0
- data/test/sass/conversion_test.rb +884 -0
- data/test/sass/css2sass_test.rb +99 -18
- data/test/sass/data/hsl-rgb.txt +319 -0
- data/test/sass/engine_test.rb +1049 -131
- data/test/sass/functions_test.rb +398 -47
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more_import.sass +3 -3
- data/test/sass/plugin_test.rb +184 -10
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +5 -5
- data/test/sass/results/compressed.css +1 -1
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +3 -1
- data/test/sass/results/mixins.css +12 -12
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/options.css +1 -0
- data/test/sass/results/parent_ref.css +4 -4
- data/test/sass/results/script.css +3 -3
- data/test/sass/results/scss_import.css +15 -0
- data/test/sass/results/scss_importee.css +2 -0
- data/test/sass/script_conversion_test.rb +153 -0
- data/test/sass/script_test.rb +137 -70
- data/test/sass/scss/css_test.rb +811 -0
- data/test/sass/scss/rx_test.rb +156 -0
- data/test/sass/scss/scss_test.rb +871 -0
- data/test/sass/scss/test_helper.rb +37 -0
- data/test/sass/templates/alt.sass +2 -2
- data/test/sass/templates/bork1.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/bork4.sass +2 -0
- data/test/sass/templates/import.sass +4 -4
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/line_numbers.sass +1 -1
- data/test/sass/templates/mixin_bork.sass +5 -0
- data/test/sass/templates/mixins.sass +2 -2
- data/test/sass/templates/nested_bork1.sass +2 -0
- data/test/sass/templates/nested_bork2.sass +2 -0
- data/test/sass/templates/nested_bork3.sass +2 -0
- data/test/sass/templates/nested_bork4.sass +2 -0
- data/test/sass/templates/nested_mixin_bork.sass +6 -0
- data/test/sass/templates/options.sass +2 -0
- data/test/sass/templates/parent_ref.sass +2 -2
- data/test/sass/templates/script.sass +69 -69
- data/test/sass/templates/scss_import.scss +10 -0
- data/test/sass/templates/scss_importee.scss +1 -0
- data/test/sass/templates/units.sass +10 -10
- data/test/test_helper.rb +20 -8
- data/vendor/fssm/LICENSE +20 -0
- data/vendor/fssm/README.markdown +55 -0
- data/vendor/fssm/Rakefile +59 -0
- data/vendor/fssm/VERSION.yml +5 -0
- data/vendor/fssm/example.rb +9 -0
- data/vendor/fssm/fssm.gemspec +77 -0
- data/vendor/fssm/lib/fssm.rb +33 -0
- data/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
- data/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
- data/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
- data/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
- data/vendor/fssm/lib/fssm/monitor.rb +26 -0
- data/vendor/fssm/lib/fssm/path.rb +91 -0
- data/vendor/fssm/lib/fssm/pathname.rb +502 -0
- data/vendor/fssm/lib/fssm/state/directory.rb +57 -0
- data/vendor/fssm/lib/fssm/state/file.rb +24 -0
- data/vendor/fssm/lib/fssm/support.rb +63 -0
- data/vendor/fssm/lib/fssm/tree.rb +176 -0
- data/vendor/fssm/profile/prof-cache.rb +40 -0
- data/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
- data/vendor/fssm/profile/prof-pathname.rb +68 -0
- data/vendor/fssm/profile/prof-plain-pathname.html +988 -0
- data/vendor/fssm/profile/prof.html +2379 -0
- data/vendor/fssm/spec/path_spec.rb +75 -0
- 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 +14 -0
- metadata +94 -14
- data/test/sass/templates/bork.sass +0 -2
data/lib/sass/script/node.rb
CHANGED
@@ -3,12 +3,90 @@ module Sass::Script
|
|
3
3
|
#
|
4
4
|
# Use \{#perform} to evaluate a parse tree.
|
5
5
|
class Node
|
6
|
+
# The options hash for this node.
|
7
|
+
#
|
8
|
+
# @return [{Symbol => Object}]
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
# The context in which this node was parsed,
|
12
|
+
# which determines how some operations are performed.
|
13
|
+
#
|
14
|
+
# Can be `:equals`, which means it's part of a `$var = val` or `prop = val` assignment,
|
15
|
+
# or `:default`, which means it's anywhere else
|
16
|
+
# (including `$var: val` and `prop: val` assignments,
|
17
|
+
# `#{}`-interpolations,
|
18
|
+
# and other script contexts such as `@if` conditions).
|
19
|
+
#
|
20
|
+
# @return [Symbol]
|
21
|
+
attr_reader :context
|
22
|
+
|
23
|
+
# The line of the document on which this node appeared.
|
24
|
+
#
|
25
|
+
# @return [Fixnum]
|
26
|
+
attr_accessor :line
|
27
|
+
|
28
|
+
# Sets the options hash for this node,
|
29
|
+
# as well as for all child nodes.
|
30
|
+
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
|
31
|
+
#
|
32
|
+
# @param options [{Symbol => Object}] The options
|
33
|
+
def options=(options)
|
34
|
+
@options = options
|
35
|
+
children.each {|c| c.options = options}
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the options hash for this node,
|
39
|
+
# as well as for all child nodes.
|
40
|
+
#
|
41
|
+
# @param context [Symbol]
|
42
|
+
# @see #context
|
43
|
+
def context=(context)
|
44
|
+
@context = context
|
45
|
+
children.each {|c| c.context = context}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates a new script node.
|
49
|
+
def initialize
|
50
|
+
@context = :default
|
51
|
+
end
|
52
|
+
|
6
53
|
# Evaluates the node.
|
7
54
|
#
|
55
|
+
# \{#perform} shouldn't be overridden directly;
|
56
|
+
# instead, override \{#\_perform}.
|
57
|
+
#
|
8
58
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
9
59
|
# @return [Literal] The SassScript object that is the value of the SassScript
|
10
60
|
def perform(environment)
|
11
|
-
|
61
|
+
_perform(environment)
|
62
|
+
rescue Sass::SyntaxError => e
|
63
|
+
e.modify_backtrace(:line => line)
|
64
|
+
raise e
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns all child nodes of this node.
|
68
|
+
#
|
69
|
+
# @return [Array<Node>]
|
70
|
+
def children
|
71
|
+
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #children.")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns the text of this SassScript expression.
|
75
|
+
#
|
76
|
+
# @return [String]
|
77
|
+
def to_sass
|
78
|
+
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #to_sass.")
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
|
83
|
+
# Evaluates this node.
|
84
|
+
#
|
85
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
86
|
+
# @return [Literal] The SassScript object that is the value of the SassScript
|
87
|
+
# @see #perform
|
88
|
+
def _perform(environment)
|
89
|
+
raise NotImplementedError.new("All subclasses of Sass::Script::Node must override #_perform.")
|
12
90
|
end
|
13
91
|
end
|
14
92
|
end
|
data/lib/sass/script/number.rb
CHANGED
@@ -25,6 +25,8 @@ module Sass::Script
|
|
25
25
|
# @return [Array<String>]
|
26
26
|
attr_reader :denominator_units
|
27
27
|
|
28
|
+
attr_accessor :original
|
29
|
+
|
28
30
|
# The precision with which numbers will be printed to CSS files.
|
29
31
|
# For example, if this is `1000.0`,
|
30
32
|
# `3.1415926` will be printed as `3.142`.
|
@@ -65,7 +67,7 @@ module Sass::Script
|
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
68
|
-
# The SassScript binary `-` operation (e.g.
|
70
|
+
# The SassScript binary `-` operation (e.g. `$a - $b`).
|
69
71
|
# Its functionality depends on the type of its argument:
|
70
72
|
#
|
71
73
|
# {Number}
|
@@ -85,7 +87,14 @@ module Sass::Script
|
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
# The SassScript unary
|
90
|
+
# The SassScript unary `+` operation (e.g. `+$a`).
|
91
|
+
#
|
92
|
+
# @return [Number] The value of this number
|
93
|
+
def unary_plus
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
# The SassScript unary `-` operation (e.g. `-$a`).
|
89
98
|
#
|
90
99
|
# @return [Number] The negative value of this number
|
91
100
|
def unary_minus
|
@@ -106,7 +115,7 @@ module Sass::Script
|
|
106
115
|
# @raise [NoMethodError] if `other` is an invalid type
|
107
116
|
def times(other)
|
108
117
|
if other.is_a? Number
|
109
|
-
operate(other, :*)
|
118
|
+
self.operate(other, :*)
|
110
119
|
elsif other.is_a? Color
|
111
120
|
other.times(self)
|
112
121
|
else
|
@@ -127,7 +136,11 @@ module Sass::Script
|
|
127
136
|
# @return [Literal] The result of the operation
|
128
137
|
def div(other)
|
129
138
|
if other.is_a? Number
|
130
|
-
operate(other, :/)
|
139
|
+
res = operate(other, :/)
|
140
|
+
if self.original && other.original && context != :equals
|
141
|
+
res.original = "#{self.original}/#{other.original}"
|
142
|
+
end
|
143
|
+
res
|
131
144
|
else
|
132
145
|
super
|
133
146
|
end
|
@@ -214,6 +227,7 @@ module Sass::Script
|
|
214
227
|
# @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
|
215
228
|
# (e.g. `px*in`)
|
216
229
|
def to_s
|
230
|
+
return original if original
|
217
231
|
raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
|
218
232
|
inspect
|
219
233
|
end
|
@@ -235,6 +249,7 @@ module Sass::Script
|
|
235
249
|
end
|
236
250
|
"#{value}#{unit_str}"
|
237
251
|
end
|
252
|
+
alias_method :to_sass, :inspect
|
238
253
|
|
239
254
|
# @return [Fixnum] The integer value of the number
|
240
255
|
# @raise [Sass::SyntaxError] if the number isn't an integer
|
@@ -284,7 +299,7 @@ module Sass::Script
|
|
284
299
|
end, num_units, den_units)
|
285
300
|
end
|
286
301
|
|
287
|
-
|
302
|
+
protected
|
288
303
|
|
289
304
|
def operate(other, operation)
|
290
305
|
this = self
|
@@ -4,11 +4,16 @@ require 'sass/script/number'
|
|
4
4
|
require 'sass/script/color'
|
5
5
|
require 'sass/script/functions'
|
6
6
|
require 'sass/script/unary_operation'
|
7
|
+
require 'sass/script/interpolation'
|
7
8
|
|
8
9
|
module Sass::Script
|
9
10
|
# A SassScript parse node representing a binary operation,
|
10
|
-
# such as
|
11
|
+
# such as `$a + $b` or `"foo" + 1`.
|
11
12
|
class Operation < Node
|
13
|
+
attr_reader :operand1
|
14
|
+
attr_reader :operand2
|
15
|
+
attr_reader :operator
|
16
|
+
|
12
17
|
# @param operand1 [Script::Node] The parse-tree node
|
13
18
|
# for the right-hand side of the operator
|
14
19
|
# @param operand2 [Script::Node] The parse-tree node
|
@@ -19,6 +24,7 @@ module Sass::Script
|
|
19
24
|
@operand1 = operand1
|
20
25
|
@operand2 = operand2
|
21
26
|
@operator = operator
|
27
|
+
super()
|
22
28
|
end
|
23
29
|
|
24
30
|
# @return [String] A human-readable s-expression representation of the operation
|
@@ -26,20 +32,60 @@ module Sass::Script
|
|
26
32
|
"(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
|
27
33
|
end
|
28
34
|
|
35
|
+
# @see Node#to_sass
|
36
|
+
def to_sass
|
37
|
+
pred = Sass::Script::Parser.precedence_of(@operator)
|
38
|
+
o1 = operand_to_sass pred, @operand1
|
39
|
+
o2 = operand_to_sass pred, @operand2
|
40
|
+
sep =
|
41
|
+
case @operator
|
42
|
+
when :comma; ", "
|
43
|
+
when :concat; " "
|
44
|
+
else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
|
45
|
+
end
|
46
|
+
"#{o1}#{sep}#{o2}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the operands for this operation.
|
50
|
+
#
|
51
|
+
# @return [Array<Node>]
|
52
|
+
# @see Node#children
|
53
|
+
def children
|
54
|
+
[@operand1, @operand2]
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
29
59
|
# Evaluates the operation.
|
30
60
|
#
|
31
61
|
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
32
62
|
# @return [Literal] The SassScript object that is the value of the operation
|
33
63
|
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
|
34
|
-
def
|
64
|
+
def _perform(environment)
|
35
65
|
literal1 = @operand1.perform(environment)
|
36
66
|
literal2 = @operand2.perform(environment)
|
67
|
+
|
68
|
+
if @operator == :concat && context == :equals
|
69
|
+
literal1 = Sass::Script::String.new(literal1.value) if literal1.is_a?(Sass::Script::String)
|
70
|
+
literal2 = Sass::Script::String.new(literal2.value) if literal2.is_a?(Sass::Script::String)
|
71
|
+
end
|
72
|
+
|
37
73
|
begin
|
38
|
-
literal1.send(@operator, literal2)
|
74
|
+
res = literal1.send(@operator, literal2)
|
75
|
+
res.options = environment.options
|
76
|
+
res
|
39
77
|
rescue NoMethodError => e
|
40
78
|
raise e unless e.name.to_s == @operator.to_s
|
41
79
|
raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
|
42
80
|
end
|
43
81
|
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def operand_to_sass(pred, op)
|
86
|
+
return "(#{op.to_sass})" if op.is_a?(Operation) &&
|
87
|
+
Sass::Script::Parser.precedence_of(op.operator) < pred
|
88
|
+
op.to_sass
|
89
|
+
end
|
44
90
|
end
|
45
91
|
end
|
data/lib/sass/script/parser.rb
CHANGED
@@ -5,16 +5,23 @@ module Sass
|
|
5
5
|
# The parser for SassScript.
|
6
6
|
# It parses a string of code into a tree of {Script::Node}s.
|
7
7
|
class Parser
|
8
|
+
# The line number of the parser's current position.
|
9
|
+
#
|
10
|
+
# @return [Fixnum]
|
11
|
+
def line
|
12
|
+
@lexer.line
|
13
|
+
end
|
14
|
+
|
8
15
|
# @param str [String, StringScanner] The source text to parse
|
9
16
|
# @param line [Fixnum] The line on which the SassScript appears.
|
10
17
|
# Used for error reporting
|
11
18
|
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
|
12
19
|
# Used for error reporting
|
13
|
-
# @param
|
14
|
-
#
|
15
|
-
def initialize(str, line, offset,
|
16
|
-
@
|
17
|
-
@lexer =
|
20
|
+
# @param options [{Symbol => Object}] An options hash;
|
21
|
+
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
|
22
|
+
def initialize(str, line, offset, options = {})
|
23
|
+
@options = options
|
24
|
+
@lexer = lexer_class.new(str, line, offset, options)
|
18
25
|
end
|
19
26
|
|
20
27
|
# Parses a SassScript expression within an interpolated segment (`#{}`).
|
@@ -27,7 +34,11 @@ module Sass
|
|
27
34
|
def parse_interpolated
|
28
35
|
expr = assert_expr :expr
|
29
36
|
assert_tok :end_interpolation
|
37
|
+
expr.options = @options
|
30
38
|
expr
|
39
|
+
rescue Sass::SyntaxError => e
|
40
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
41
|
+
raise e
|
31
42
|
end
|
32
43
|
|
33
44
|
# Parses a SassScript expression.
|
@@ -37,7 +48,28 @@ module Sass
|
|
37
48
|
def parse
|
38
49
|
expr = assert_expr :expr
|
39
50
|
assert_done
|
51
|
+
expr.options = @options
|
40
52
|
expr
|
53
|
+
rescue Sass::SyntaxError => e
|
54
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
55
|
+
raise e
|
56
|
+
end
|
57
|
+
|
58
|
+
# Parses a SassScript expression,
|
59
|
+
# ending it when it encounters one of the given identifier tokens.
|
60
|
+
#
|
61
|
+
# @param [#include?(String)] A set of strings that delimit the expression.
|
62
|
+
# @return [Script::Node] The root node of the parse tree
|
63
|
+
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
|
64
|
+
def parse_until(tokens)
|
65
|
+
@stop_at = tokens
|
66
|
+
expr = assert_expr :expr
|
67
|
+
assert_done
|
68
|
+
expr.options = @options
|
69
|
+
expr
|
70
|
+
rescue Sass::SyntaxError => e
|
71
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
72
|
+
raise e
|
41
73
|
end
|
42
74
|
|
43
75
|
# Parses the argument list for a mixin include.
|
@@ -53,7 +85,11 @@ module Sass
|
|
53
85
|
end
|
54
86
|
assert_done
|
55
87
|
|
88
|
+
args.each {|a| a.options = @options}
|
56
89
|
args
|
90
|
+
rescue Sass::SyntaxError => e
|
91
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
92
|
+
raise e
|
57
93
|
end
|
58
94
|
|
59
95
|
# Parses the argument list for a mixin definition.
|
@@ -69,7 +105,14 @@ module Sass
|
|
69
105
|
end
|
70
106
|
assert_done
|
71
107
|
|
108
|
+
args.each do |k, v|
|
109
|
+
k.options = @options
|
110
|
+
v.options = @options if v
|
111
|
+
end
|
72
112
|
args
|
113
|
+
rescue Sass::SyntaxError => e
|
114
|
+
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
115
|
+
raise e
|
73
116
|
end
|
74
117
|
|
75
118
|
# Parses a SassScript expression.
|
@@ -82,7 +125,28 @@ module Sass
|
|
82
125
|
new(*args).parse
|
83
126
|
end
|
84
127
|
|
128
|
+
# @private
|
129
|
+
PRECEDENCE = [
|
130
|
+
:comma, :concat, :or, :and,
|
131
|
+
[:eq, :neq],
|
132
|
+
[:gt, :gte, :lt, :lte],
|
133
|
+
[:plus, :minus],
|
134
|
+
[:times, :div, :mod],
|
135
|
+
]
|
136
|
+
|
85
137
|
class << self
|
138
|
+
# Returns an integer representing the precedence
|
139
|
+
# of the given operator.
|
140
|
+
# A lower integer indicates a looser binding.
|
141
|
+
#
|
142
|
+
# @private
|
143
|
+
def precedence_of(op)
|
144
|
+
PRECEDENCE.each_with_index do |e, i|
|
145
|
+
return i if Array(e).include?(op)
|
146
|
+
end
|
147
|
+
raise "[BUG] Unknown operator #{op}"
|
148
|
+
end
|
149
|
+
|
86
150
|
private
|
87
151
|
|
88
152
|
# Defines a simple left-associative production.
|
@@ -94,7 +158,9 @@ module Sass
|
|
94
158
|
def #{name}
|
95
159
|
return unless e = #{sub}
|
96
160
|
while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
|
161
|
+
line = @lexer.line
|
97
162
|
e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
|
163
|
+
e.line = line
|
98
164
|
end
|
99
165
|
e
|
100
166
|
end
|
@@ -105,7 +171,10 @@ RUBY
|
|
105
171
|
class_eval <<RUBY
|
106
172
|
def unary_#{op}
|
107
173
|
return #{sub} unless try_tok(:#{op})
|
108
|
-
|
174
|
+
line = @lexer.line
|
175
|
+
op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
|
176
|
+
op.line = line
|
177
|
+
op
|
109
178
|
end
|
110
179
|
RUBY
|
111
180
|
end
|
@@ -113,12 +182,28 @@ RUBY
|
|
113
182
|
|
114
183
|
private
|
115
184
|
|
116
|
-
|
185
|
+
# @private
|
186
|
+
def lexer_class; Lexer; end
|
187
|
+
|
188
|
+
production :expr, :interpolation, :comma
|
189
|
+
|
190
|
+
def interpolation
|
191
|
+
e = concat
|
192
|
+
while interp = try_tok(:begin_interpolation)
|
193
|
+
wb = @lexer.whitespace?(interp)
|
194
|
+
line = @lexer.line
|
195
|
+
mid = parse_interpolated
|
196
|
+
wa = @lexer.whitespace?
|
197
|
+
e = Script::Interpolation.new(e, mid, concat, wb, wa)
|
198
|
+
e.line = line
|
199
|
+
end
|
200
|
+
e
|
201
|
+
end
|
117
202
|
|
118
203
|
def concat
|
119
204
|
return unless e = or_expr
|
120
205
|
while sub = or_expr
|
121
|
-
e = Operation.new(e, sub, :concat)
|
206
|
+
e = node(Operation.new(e, sub, :concat))
|
122
207
|
end
|
123
208
|
e
|
124
209
|
end
|
@@ -128,37 +213,47 @@ RUBY
|
|
128
213
|
production :eq_or_neq, :relational, :eq, :neq
|
129
214
|
production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
|
130
215
|
production :plus_or_minus, :times_div_or_mod, :plus, :minus
|
131
|
-
production :times_div_or_mod, :
|
216
|
+
production :times_div_or_mod, :unary_plus, :times, :div, :mod
|
132
217
|
|
218
|
+
unary :plus, :unary_minus
|
133
219
|
unary :minus, :unary_div
|
134
220
|
unary :div, :unary_not # For strings, so /foo/bar works
|
135
221
|
unary :not, :funcall
|
136
222
|
|
137
223
|
def funcall
|
138
|
-
return
|
224
|
+
return raw unless @lexer.peek && @lexer.peek.type == :ident
|
225
|
+
return if @stop_at && @stop_at.include?(@lexer.peek.value)
|
226
|
+
|
227
|
+
name = @lexer.next
|
139
228
|
# An identifier without arguments is just a string
|
140
229
|
unless try_tok(:lparen)
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
'#{name.value}' was not quoted. Please add double quotes (e.g. "#{name.value}").
|
146
|
-
END
|
147
|
-
Script::String.new(name.value)
|
230
|
+
if color = Color::HTML4_COLORS[name.value]
|
231
|
+
return node(Color.new(color))
|
232
|
+
end
|
233
|
+
node(Script::String.new(name.value, :identifier))
|
148
234
|
else
|
149
235
|
args = arglist || []
|
150
236
|
assert_tok(:rparen)
|
151
|
-
Script::Funcall.new(name.value, args)
|
237
|
+
node(Script::Funcall.new(name.value, args))
|
152
238
|
end
|
153
239
|
end
|
154
240
|
|
155
241
|
def defn_arglist(must_have_default)
|
242
|
+
line = @lexer.line
|
243
|
+
offset = @lexer.offset + 1
|
156
244
|
return unless c = try_tok(:const)
|
157
245
|
var = Script::Variable.new(c.value)
|
158
|
-
if try_tok(:single_eq)
|
246
|
+
if tok = (try_tok(:colon) || try_tok(:single_eq))
|
159
247
|
val = assert_expr(:concat)
|
248
|
+
|
249
|
+
if tok.type == :single_eq
|
250
|
+
val.context = :equals
|
251
|
+
val.options = @options
|
252
|
+
Script.equals_warning("mixin argument defaults", "$#{c.value}",
|
253
|
+
val.to_sass, false, line, offset, @options[:filename])
|
254
|
+
end
|
160
255
|
elsif must_have_default
|
161
|
-
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments."
|
256
|
+
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
|
162
257
|
end
|
163
258
|
|
164
259
|
return [[var, val]] unless try_tok(:comma)
|
@@ -166,46 +261,80 @@ END
|
|
166
261
|
end
|
167
262
|
|
168
263
|
def arglist
|
169
|
-
return unless e =
|
264
|
+
return unless e = interpolation
|
170
265
|
return [e] unless try_tok(:comma)
|
171
266
|
[e, *arglist]
|
172
267
|
end
|
173
268
|
|
269
|
+
def raw
|
270
|
+
return special_fun unless tok = try_tok(:raw)
|
271
|
+
node(Script::String.new(tok.value))
|
272
|
+
end
|
273
|
+
|
274
|
+
def special_fun
|
275
|
+
return paren unless tok = try_tok(:special_fun)
|
276
|
+
first = node(Script::String.new(tok.value.first))
|
277
|
+
Haml::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
|
278
|
+
Script::Interpolation.new(
|
279
|
+
l, i, r && node(Script::String.new(r)),
|
280
|
+
false, false)
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
174
284
|
def paren
|
175
285
|
return variable unless try_tok(:lparen)
|
286
|
+
was_in_parens = @in_parens
|
287
|
+
@in_parens = true
|
176
288
|
e = assert_expr(:expr)
|
177
289
|
assert_tok(:rparen)
|
178
290
|
return e
|
291
|
+
ensure
|
292
|
+
@in_parens = was_in_parens
|
179
293
|
end
|
180
294
|
|
181
295
|
def variable
|
182
296
|
return string unless c = try_tok(:const)
|
183
|
-
Variable.new(c.value)
|
297
|
+
node(Variable.new(*c.value))
|
184
298
|
end
|
185
299
|
|
186
300
|
def string
|
187
|
-
return
|
301
|
+
return number unless first = try_tok(:string)
|
188
302
|
return first.value unless try_tok(:begin_interpolation)
|
303
|
+
line = @lexer.line
|
189
304
|
mid = parse_interpolated
|
190
305
|
last = assert_expr(:string)
|
191
|
-
Operation.new(first.value, Operation.new(mid, last, :plus), :plus)
|
306
|
+
op = Operation.new(first.value, node(Operation.new(mid, last, :plus)), :plus)
|
307
|
+
op.line = line
|
308
|
+
op
|
309
|
+
end
|
310
|
+
|
311
|
+
def number
|
312
|
+
return literal unless tok = try_tok(:number)
|
313
|
+
num = tok.value
|
314
|
+
num.original = num.to_s unless @in_parens
|
315
|
+
num
|
192
316
|
end
|
193
317
|
|
194
318
|
def literal
|
195
|
-
(t = try_tok(:
|
319
|
+
(t = try_tok(:color, :bool)) && (return t.value)
|
196
320
|
end
|
197
321
|
|
198
322
|
# It would be possible to have unified #assert and #try methods,
|
199
323
|
# but detecting the method/token difference turns out to be quite expensive.
|
200
324
|
|
325
|
+
EXPR_NAMES = {
|
326
|
+
:string => "string",
|
327
|
+
:default => "expression (e.g. 1px, bold)",
|
328
|
+
}
|
329
|
+
|
201
330
|
def assert_expr(name)
|
202
331
|
(e = send(name)) && (return e)
|
203
|
-
|
332
|
+
@lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default])
|
204
333
|
end
|
205
334
|
|
206
335
|
def assert_tok(*names)
|
207
336
|
(t = try_tok(*names)) && (return t)
|
208
|
-
|
337
|
+
@lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
|
209
338
|
end
|
210
339
|
|
211
340
|
def try_tok(*names)
|
@@ -215,7 +344,12 @@ END
|
|
215
344
|
|
216
345
|
def assert_done
|
217
346
|
return if @lexer.done?
|
218
|
-
|
347
|
+
@lexer.expected!(EXPR_NAMES[:default])
|
348
|
+
end
|
349
|
+
|
350
|
+
def node(node)
|
351
|
+
node.line = @lexer.line
|
352
|
+
node
|
219
353
|
end
|
220
354
|
end
|
221
355
|
end
|