haml-edge 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/EDGE_GEM_VERSION +1 -0
- data/FAQ +138 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +332 -0
- data/REVISION +1 -0
- data/Rakefile +226 -0
- data/VERSION +1 -0
- data/bin/css2sass +7 -0
- data/bin/haml +9 -0
- data/bin/html2haml +7 -0
- data/bin/sass +8 -0
- data/extra/edge_gem_watch.rb +13 -0
- data/extra/haml-mode.el +596 -0
- data/extra/sass-mode.el +200 -0
- data/init.rb +8 -0
- data/lib/haml/buffer.rb +255 -0
- data/lib/haml/engine.rb +268 -0
- data/lib/haml/error.rb +22 -0
- data/lib/haml/exec.rb +395 -0
- data/lib/haml/filters.rb +275 -0
- data/lib/haml/helpers/action_view_extensions.rb +45 -0
- data/lib/haml/helpers/action_view_mods.rb +181 -0
- data/lib/haml/helpers.rb +488 -0
- data/lib/haml/html.rb +222 -0
- data/lib/haml/precompiler.rb +904 -0
- data/lib/haml/shared.rb +45 -0
- data/lib/haml/template/patch.rb +58 -0
- data/lib/haml/template/plugin.rb +72 -0
- data/lib/haml/template.rb +42 -0
- data/lib/haml/util.rb +88 -0
- data/lib/haml/version.rb +47 -0
- data/lib/haml.rb +1044 -0
- data/lib/sass/css.rb +388 -0
- data/lib/sass/engine.rb +495 -0
- data/lib/sass/environment.rb +46 -0
- data/lib/sass/error.rb +35 -0
- data/lib/sass/plugin/merb.rb +56 -0
- data/lib/sass/plugin/rails.rb +24 -0
- data/lib/sass/plugin.rb +204 -0
- data/lib/sass/repl.rb +51 -0
- data/lib/sass/script/bool.rb +13 -0
- data/lib/sass/script/color.rb +97 -0
- data/lib/sass/script/funcall.rb +29 -0
- data/lib/sass/script/functions.rb +134 -0
- data/lib/sass/script/lexer.rb +148 -0
- data/lib/sass/script/literal.rb +82 -0
- data/lib/sass/script/number.rb +231 -0
- data/lib/sass/script/operation.rb +30 -0
- data/lib/sass/script/parser.rb +142 -0
- data/lib/sass/script/string.rb +9 -0
- data/lib/sass/script/unary_operation.rb +21 -0
- data/lib/sass/script/variable.rb +20 -0
- data/lib/sass/script.rb +38 -0
- data/lib/sass/tree/attr_node.rb +64 -0
- data/lib/sass/tree/comment_node.rb +30 -0
- data/lib/sass/tree/debug_node.rb +22 -0
- data/lib/sass/tree/directive_node.rb +50 -0
- data/lib/sass/tree/file_node.rb +27 -0
- data/lib/sass/tree/for_node.rb +29 -0
- data/lib/sass/tree/if_node.rb +27 -0
- data/lib/sass/tree/mixin_def_node.rb +18 -0
- data/lib/sass/tree/mixin_node.rb +35 -0
- data/lib/sass/tree/node.rb +99 -0
- data/lib/sass/tree/rule_node.rb +161 -0
- data/lib/sass/tree/variable_node.rb +24 -0
- data/lib/sass/tree/while_node.rb +21 -0
- data/lib/sass.rb +1062 -0
- data/rails/init.rb +1 -0
- data/test/benchmark.rb +99 -0
- data/test/haml/engine_test.rb +795 -0
- data/test/haml/helper_test.rb +228 -0
- data/test/haml/html2haml_test.rb +108 -0
- data/test/haml/markaby/standard.mab +52 -0
- data/test/haml/mocks/article.rb +6 -0
- data/test/haml/results/content_for_layout.xhtml +15 -0
- data/test/haml/results/eval_suppressed.xhtml +9 -0
- data/test/haml/results/filters.xhtml +62 -0
- data/test/haml/results/helpers.xhtml +93 -0
- data/test/haml/results/helpful.xhtml +10 -0
- data/test/haml/results/just_stuff.xhtml +68 -0
- data/test/haml/results/list.xhtml +12 -0
- data/test/haml/results/nuke_inner_whitespace.xhtml +40 -0
- data/test/haml/results/nuke_outer_whitespace.xhtml +148 -0
- data/test/haml/results/original_engine.xhtml +20 -0
- data/test/haml/results/partial_layout.xhtml +5 -0
- data/test/haml/results/partials.xhtml +21 -0
- data/test/haml/results/render_layout.xhtml +3 -0
- data/test/haml/results/silent_script.xhtml +74 -0
- data/test/haml/results/standard.xhtml +162 -0
- data/test/haml/results/tag_parsing.xhtml +23 -0
- data/test/haml/results/very_basic.xhtml +5 -0
- data/test/haml/results/whitespace_handling.xhtml +89 -0
- data/test/haml/rhtml/_av_partial_1.rhtml +12 -0
- data/test/haml/rhtml/_av_partial_2.rhtml +8 -0
- data/test/haml/rhtml/action_view.rhtml +62 -0
- data/test/haml/rhtml/standard.rhtml +54 -0
- data/test/haml/template_test.rb +204 -0
- data/test/haml/templates/_av_partial_1.haml +9 -0
- data/test/haml/templates/_av_partial_1_ugly.haml +9 -0
- data/test/haml/templates/_av_partial_2.haml +5 -0
- data/test/haml/templates/_av_partial_2_ugly.haml +5 -0
- data/test/haml/templates/_layout.erb +3 -0
- data/test/haml/templates/_layout_for_partial.haml +3 -0
- data/test/haml/templates/_partial.haml +8 -0
- data/test/haml/templates/_text_area.haml +3 -0
- data/test/haml/templates/action_view.haml +47 -0
- data/test/haml/templates/action_view_ugly.haml +47 -0
- data/test/haml/templates/breakage.haml +8 -0
- data/test/haml/templates/content_for_layout.haml +10 -0
- data/test/haml/templates/eval_suppressed.haml +11 -0
- data/test/haml/templates/filters.haml +66 -0
- data/test/haml/templates/helpers.haml +95 -0
- data/test/haml/templates/helpful.haml +11 -0
- data/test/haml/templates/just_stuff.haml +83 -0
- data/test/haml/templates/list.haml +12 -0
- data/test/haml/templates/nuke_inner_whitespace.haml +32 -0
- data/test/haml/templates/nuke_outer_whitespace.haml +144 -0
- data/test/haml/templates/original_engine.haml +17 -0
- data/test/haml/templates/partial_layout.haml +3 -0
- data/test/haml/templates/partialize.haml +1 -0
- data/test/haml/templates/partials.haml +12 -0
- data/test/haml/templates/render_layout.haml +2 -0
- data/test/haml/templates/silent_script.haml +40 -0
- data/test/haml/templates/standard.haml +42 -0
- data/test/haml/templates/standard_ugly.haml +42 -0
- data/test/haml/templates/tag_parsing.haml +21 -0
- data/test/haml/templates/very_basic.haml +4 -0
- data/test/haml/templates/whitespace_handling.haml +87 -0
- data/test/haml/util_test.rb +87 -0
- data/test/linked_rails.rb +12 -0
- data/test/sass/css2sass_test.rb +193 -0
- data/test/sass/engine_test.rb +709 -0
- data/test/sass/functions_test.rb +109 -0
- data/test/sass/more_results/more1.css +9 -0
- data/test/sass/more_results/more1_with_line_comments.css +26 -0
- data/test/sass/more_results/more_import.css +29 -0
- data/test/sass/more_templates/_more_partial.sass +2 -0
- data/test/sass/more_templates/more1.sass +23 -0
- data/test/sass/more_templates/more_import.sass +11 -0
- data/test/sass/plugin_test.rb +213 -0
- data/test/sass/results/alt.css +4 -0
- data/test/sass/results/basic.css +9 -0
- data/test/sass/results/compact.css +5 -0
- data/test/sass/results/complex.css +87 -0
- data/test/sass/results/compressed.css +1 -0
- data/test/sass/results/expanded.css +19 -0
- data/test/sass/results/import.css +29 -0
- data/test/sass/results/line_numbers.css +49 -0
- data/test/sass/results/mixins.css +95 -0
- data/test/sass/results/multiline.css +24 -0
- data/test/sass/results/nested.css +22 -0
- data/test/sass/results/parent_ref.css +13 -0
- data/test/sass/results/script.css +16 -0
- data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
- data/test/sass/results/subdir/subdir.css +3 -0
- data/test/sass/results/units.css +11 -0
- data/test/sass/script_test.rb +250 -0
- data/test/sass/templates/_partial.sass +2 -0
- data/test/sass/templates/alt.sass +16 -0
- data/test/sass/templates/basic.sass +23 -0
- data/test/sass/templates/bork.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +309 -0
- data/test/sass/templates/compressed.sass +15 -0
- data/test/sass/templates/expanded.sass +17 -0
- data/test/sass/templates/import.sass +11 -0
- data/test/sass/templates/importee.sass +19 -0
- data/test/sass/templates/line_numbers.sass +13 -0
- data/test/sass/templates/mixins.sass +76 -0
- data/test/sass/templates/multiline.sass +20 -0
- data/test/sass/templates/nested.sass +25 -0
- data/test/sass/templates/parent_ref.sass +25 -0
- data/test/sass/templates/script.sass +101 -0
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
- data/test/sass/templates/subdir/subdir.sass +6 -0
- data/test/sass/templates/units.sass +11 -0
- data/test/test_helper.rb +21 -0
- metadata +278 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module Sass::Script
|
2
|
+
class Literal # :nodoc:
|
3
|
+
require 'sass/script/string'
|
4
|
+
require 'sass/script/number'
|
5
|
+
require 'sass/script/color'
|
6
|
+
require 'sass/script/bool'
|
7
|
+
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
def initialize(value = nil)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform(environment)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def and(other)
|
19
|
+
to_bool ? other : self
|
20
|
+
end
|
21
|
+
|
22
|
+
def or(other)
|
23
|
+
to_bool ? self : other
|
24
|
+
end
|
25
|
+
|
26
|
+
def eq(other)
|
27
|
+
Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
|
28
|
+
end
|
29
|
+
|
30
|
+
def neq(other)
|
31
|
+
Sass::Script::Bool.new(!eq(other).to_bool)
|
32
|
+
end
|
33
|
+
|
34
|
+
def unary_not
|
35
|
+
Sass::Script::Bool.new(!to_bool)
|
36
|
+
end
|
37
|
+
|
38
|
+
def concat(other)
|
39
|
+
Sass::Script::String.new("#{self.to_s} #{other.to_s}")
|
40
|
+
end
|
41
|
+
|
42
|
+
def comma(other)
|
43
|
+
Sass::Script::String.new("#{self.to_s}, #{other.to_s}")
|
44
|
+
end
|
45
|
+
|
46
|
+
def plus(other)
|
47
|
+
Sass::Script::String.new(self.to_s + other.to_s)
|
48
|
+
end
|
49
|
+
|
50
|
+
def minus(other)
|
51
|
+
Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def div(other)
|
55
|
+
Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def unary_minus
|
59
|
+
Sass::Script::String.new("-#{self.to_s}")
|
60
|
+
end
|
61
|
+
|
62
|
+
def unary_div
|
63
|
+
Sass::Script::String.new("/#{self.to_s}")
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
value.inspect
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_bool
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
def ==(other)
|
75
|
+
eq(other).to_bool
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_i
|
79
|
+
raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'sass/script/literal'
|
2
|
+
|
3
|
+
module Sass::Script
|
4
|
+
class Number < Literal # :nodoc:
|
5
|
+
|
6
|
+
attr_reader :numerator_units, :denominator_units
|
7
|
+
|
8
|
+
PRECISION = 1000.0
|
9
|
+
|
10
|
+
def initialize(value, numerator_units = [], denominator_units = [])
|
11
|
+
super(value)
|
12
|
+
@numerator_units = numerator_units
|
13
|
+
@denominator_units = denominator_units
|
14
|
+
normalize!
|
15
|
+
end
|
16
|
+
|
17
|
+
def plus(other)
|
18
|
+
if other.is_a? Number
|
19
|
+
operate(other, :+)
|
20
|
+
elsif other.is_a?(Color)
|
21
|
+
other.plus(self)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def minus(other)
|
28
|
+
if other.is_a? Number
|
29
|
+
operate(other, :-)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def unary_minus
|
36
|
+
Number.new(-value, numerator_units, denominator_units)
|
37
|
+
end
|
38
|
+
|
39
|
+
def times(other)
|
40
|
+
if other.is_a? Number
|
41
|
+
self.operate(other, :*)
|
42
|
+
elsif other.is_a? Color
|
43
|
+
other.times(self)
|
44
|
+
else
|
45
|
+
raise NoMethodError.new(nil, :times)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def div(other)
|
50
|
+
if other.is_a? Number
|
51
|
+
operate(other, :/)
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def mod(other)
|
58
|
+
if other.is_a?(Number)
|
59
|
+
unless other.unitless?
|
60
|
+
raise Sass::SyntaxError.new("Cannot modulo by a number with units: #{other.inspect}.")
|
61
|
+
end
|
62
|
+
operate(other, :%)
|
63
|
+
else
|
64
|
+
raise NoMethodError.new(nil, :mod)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def eq(other)
|
69
|
+
Sass::Script::Bool.new(super.to_bool &&
|
70
|
+
self.numerator_units.sort == other.numerator_units.sort &&
|
71
|
+
self.denominator_units.sort == other.denominator_units.sort)
|
72
|
+
end
|
73
|
+
|
74
|
+
def gt(other)
|
75
|
+
raise NoMethodError.new(nil, :gt) unless other.is_a?(Number)
|
76
|
+
operate(other, :>)
|
77
|
+
end
|
78
|
+
|
79
|
+
def gte(other)
|
80
|
+
raise NoMethodError.new(nil, :gte) unless other.is_a?(Number)
|
81
|
+
operate(other, :>=)
|
82
|
+
end
|
83
|
+
|
84
|
+
def lt(other)
|
85
|
+
raise NoMethodError.new(nil, :lt) unless other.is_a?(Number)
|
86
|
+
operate(other, :<)
|
87
|
+
end
|
88
|
+
|
89
|
+
def lte(other)
|
90
|
+
raise NoMethodError.new(nil, :lte) unless other.is_a?(Number)
|
91
|
+
operate(other, :<=)
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_s
|
95
|
+
raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
|
96
|
+
inspect
|
97
|
+
end
|
98
|
+
|
99
|
+
def inspect
|
100
|
+
value =
|
101
|
+
if self.value.is_a?(Float) && (self.value.infinite? || self.value.nan?)
|
102
|
+
self.value
|
103
|
+
elsif int?
|
104
|
+
self.value.to_i
|
105
|
+
else
|
106
|
+
(self.value * PRECISION).round / PRECISION
|
107
|
+
end
|
108
|
+
"#{value}#{unit_str}"
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_i
|
112
|
+
super unless int?
|
113
|
+
return value
|
114
|
+
end
|
115
|
+
|
116
|
+
def int?
|
117
|
+
value % 1 == 0.0
|
118
|
+
end
|
119
|
+
|
120
|
+
def unitless?
|
121
|
+
numerator_units.empty? && denominator_units.empty?
|
122
|
+
end
|
123
|
+
|
124
|
+
def legal_units?
|
125
|
+
(numerator_units.empty? || numerator_units.size == 1) && denominator_units.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
protected
|
129
|
+
|
130
|
+
def operate(other, operation)
|
131
|
+
this = self
|
132
|
+
if [:+, :-].include?(operation)
|
133
|
+
if unitless?
|
134
|
+
this = this.coerce(other.numerator_units, other.denominator_units)
|
135
|
+
else
|
136
|
+
other = other.coerce(numerator_units, denominator_units)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
# avoid integer division
|
140
|
+
value = (:/ == operation) ? this.value.to_f : this.value
|
141
|
+
result = value.send(operation, other.value)
|
142
|
+
|
143
|
+
if result.is_a?(Numeric)
|
144
|
+
Number.new(result, *compute_units(this, other, operation))
|
145
|
+
else # Boolean op
|
146
|
+
Bool.new(result)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def coerce(num_units, den_units)
|
151
|
+
Number.new(if unitless?
|
152
|
+
self.value
|
153
|
+
else
|
154
|
+
self.value * coercion_factor(self.numerator_units, num_units) /
|
155
|
+
coercion_factor(self.denominator_units, den_units)
|
156
|
+
end, num_units, den_units)
|
157
|
+
end
|
158
|
+
|
159
|
+
def coercion_factor(from_units, to_units)
|
160
|
+
# get a list of unmatched units
|
161
|
+
from_units, to_units = sans_common_units(from_units, to_units)
|
162
|
+
|
163
|
+
if from_units.size != to_units.size || !convertable?(from_units | to_units)
|
164
|
+
raise Sass::SyntaxError.new("Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
|
165
|
+
end
|
166
|
+
|
167
|
+
from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1]) }
|
168
|
+
end
|
169
|
+
|
170
|
+
def compute_units(this, other, operation)
|
171
|
+
case operation
|
172
|
+
when :*
|
173
|
+
[this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units]
|
174
|
+
when :/
|
175
|
+
[this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units]
|
176
|
+
else
|
177
|
+
[this.numerator_units, this.denominator_units]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def unit_str
|
182
|
+
rv = numerator_units.join("*")
|
183
|
+
if denominator_units.any?
|
184
|
+
rv << "/"
|
185
|
+
rv << denominator_units.join("*")
|
186
|
+
end
|
187
|
+
rv
|
188
|
+
end
|
189
|
+
|
190
|
+
def normalize!
|
191
|
+
return if unitless?
|
192
|
+
@numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units)
|
193
|
+
|
194
|
+
@denominator_units.each_with_index do |d, i|
|
195
|
+
if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?)))
|
196
|
+
@value /= conversion_factor(d, u)
|
197
|
+
@denominator_units.delete_at(i)
|
198
|
+
@numerator_units.delete_at(@numerator_units.index(u))
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# A hash of unit names to their index in the conversion table
|
204
|
+
CONVERTABLE_UNITS = {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4}
|
205
|
+
CONVERSION_TABLE = [[ 1, 2.54, 6, 25.4, 72 ], # in
|
206
|
+
[ nil, 1, 2.36220473, 10, 28.3464567], # cm
|
207
|
+
[ nil, nil, 1, 4.23333333, 12 ], # pc
|
208
|
+
[ nil, nil, nil, 1, 2.83464567], # mm
|
209
|
+
[ nil, nil, nil, nil, 1 ]] # pt
|
210
|
+
|
211
|
+
def conversion_factor(from_unit, to_unit)
|
212
|
+
res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]]
|
213
|
+
return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
|
214
|
+
res
|
215
|
+
end
|
216
|
+
|
217
|
+
def convertable?(units)
|
218
|
+
Array(units).all?(&CONVERTABLE_UNITS.method(:include?))
|
219
|
+
end
|
220
|
+
|
221
|
+
def sans_common_units(units1, units2)
|
222
|
+
units2 = units2.dup
|
223
|
+
# Can't just use -, because we want px*px to coerce properly to px*mm
|
224
|
+
return units1.map do |u|
|
225
|
+
next u unless j = units2.index(u)
|
226
|
+
units2.delete_at(j)
|
227
|
+
nil
|
228
|
+
end.compact, units2
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'sass/script/string'
|
2
|
+
require 'sass/script/number'
|
3
|
+
require 'sass/script/color'
|
4
|
+
require 'sass/script/functions'
|
5
|
+
require 'sass/script/unary_operation'
|
6
|
+
|
7
|
+
module Sass::Script
|
8
|
+
class Operation # :nodoc:
|
9
|
+
def initialize(operand1, operand2, operator)
|
10
|
+
@operand1 = operand1
|
11
|
+
@operand2 = operand2
|
12
|
+
@operator = operator
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
|
17
|
+
end
|
18
|
+
|
19
|
+
def perform(environment)
|
20
|
+
literal1 = @operand1.perform(environment)
|
21
|
+
literal2 = @operand2.perform(environment)
|
22
|
+
begin
|
23
|
+
literal1.send(@operator, literal2)
|
24
|
+
rescue NoMethodError => e
|
25
|
+
raise e unless e.name.to_s == @operator.to_s
|
26
|
+
raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'sass/script/lexer'
|
2
|
+
|
3
|
+
module Sass
|
4
|
+
module Script
|
5
|
+
class Parser
|
6
|
+
def initialize(str, line, offset, filename = nil)
|
7
|
+
@filename = filename
|
8
|
+
@lexer = Lexer.new(str, line, offset)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_interpolated
|
12
|
+
expr = assert_expr :expr
|
13
|
+
assert_tok :end_interpolation
|
14
|
+
expr
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse
|
18
|
+
expr = assert_expr :expr
|
19
|
+
raise Sass::SyntaxError.new("Unexpected #{@lexer.peek.type} token.") unless @lexer.done?
|
20
|
+
expr
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.parse(*args)
|
24
|
+
new(*args).parse
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Defines a simple left-associative production.
|
30
|
+
# name is the name of the production,
|
31
|
+
# sub is the name of the production beneath it,
|
32
|
+
# and ops is a list of operators for this precedence level
|
33
|
+
def self.production(name, sub, *ops)
|
34
|
+
class_eval <<RUBY
|
35
|
+
def #{name}
|
36
|
+
return unless e = #{sub}
|
37
|
+
while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
|
38
|
+
e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
|
39
|
+
end
|
40
|
+
e
|
41
|
+
end
|
42
|
+
RUBY
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.unary(op, sub)
|
46
|
+
class_eval <<RUBY
|
47
|
+
def unary_#{op}
|
48
|
+
return #{sub} unless try_tok(:#{op})
|
49
|
+
UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
|
50
|
+
end
|
51
|
+
RUBY
|
52
|
+
end
|
53
|
+
|
54
|
+
production :expr, :concat, :comma
|
55
|
+
|
56
|
+
def concat
|
57
|
+
return unless e = or_expr
|
58
|
+
while sub = or_expr
|
59
|
+
e = Operation.new(e, sub, :concat)
|
60
|
+
end
|
61
|
+
e
|
62
|
+
end
|
63
|
+
|
64
|
+
production :or_expr, :and_expr, :or
|
65
|
+
production :and_expr, :eq_or_neq, :and
|
66
|
+
production :eq_or_neq, :relational, :eq, :neq
|
67
|
+
production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
|
68
|
+
production :plus_or_minus, :times_div_or_mod, :plus, :minus
|
69
|
+
production :times_div_or_mod, :unary_minus, :times, :div, :mod
|
70
|
+
|
71
|
+
unary :minus, :unary_div
|
72
|
+
unary :div, :unary_not # For strings, so /foo/bar works
|
73
|
+
unary :not, :funcall
|
74
|
+
|
75
|
+
def funcall
|
76
|
+
return paren unless name = try_tok(:ident)
|
77
|
+
# An identifier without arguments is just a string
|
78
|
+
unless try_tok(:lparen)
|
79
|
+
warn(<<END)
|
80
|
+
DEPRECATION WARNING:
|
81
|
+
On line #{name.line}, character #{name.offset}#{" of '#{@filename}'" if @filename}
|
82
|
+
Implicit strings have been deprecated and will be removed in version 2.4.
|
83
|
+
'#{name.value}' was not quoted. Please add double quotes (e.g. "#{name.value}").
|
84
|
+
END
|
85
|
+
Script::String.new(name.value)
|
86
|
+
else
|
87
|
+
args = arglist || []
|
88
|
+
assert_tok(:rparen)
|
89
|
+
Script::Funcall.new(name.value, args)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def arglist
|
94
|
+
return unless e = concat
|
95
|
+
return [e] unless try_tok(:comma)
|
96
|
+
[e, *arglist]
|
97
|
+
end
|
98
|
+
|
99
|
+
def paren
|
100
|
+
return variable unless try_tok(:lparen)
|
101
|
+
e = assert_expr(:expr)
|
102
|
+
assert_tok(:rparen)
|
103
|
+
return e
|
104
|
+
end
|
105
|
+
|
106
|
+
def variable
|
107
|
+
return string unless c = try_tok(:const)
|
108
|
+
Variable.new(c.value)
|
109
|
+
end
|
110
|
+
|
111
|
+
def string
|
112
|
+
return literal unless first = try_tok(:string)
|
113
|
+
return first.value unless try_tok(:begin_interpolation)
|
114
|
+
mid = parse_interpolated
|
115
|
+
last = assert_expr(:string)
|
116
|
+
Operation.new(first.value, Operation.new(mid, last, :plus), :plus)
|
117
|
+
end
|
118
|
+
|
119
|
+
def literal
|
120
|
+
(t = try_tok(:number, :color, :bool)) && (return t.value)
|
121
|
+
end
|
122
|
+
|
123
|
+
# It would be possible to have unified #assert and #try methods,
|
124
|
+
# but detecting the method/token difference turns out to be quite expensive.
|
125
|
+
|
126
|
+
def assert_expr(name)
|
127
|
+
(e = send(name)) && (return e)
|
128
|
+
raise Sass::SyntaxError.new("Expected expression, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.")
|
129
|
+
end
|
130
|
+
|
131
|
+
def assert_tok(*names)
|
132
|
+
(t = try_tok(*names)) && (return t)
|
133
|
+
raise Sass::SyntaxError.new("Expected #{names.join(' or ')} token, was #{@lexer.done? ? 'end of text' : "#{@lexer.peek.type} token"}.")
|
134
|
+
end
|
135
|
+
|
136
|
+
def try_tok(*names)
|
137
|
+
peeked = @lexer.peek
|
138
|
+
peeked && names.include?(peeked.type) && @lexer.next
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sass::Script
|
2
|
+
class UnaryOperation # :nodoc:
|
3
|
+
def initialize(operand, operator)
|
4
|
+
@operand = operand
|
5
|
+
@operator = operator
|
6
|
+
end
|
7
|
+
|
8
|
+
def inspect
|
9
|
+
"(#{@operator.inspect} #{@operand.inspect})"
|
10
|
+
end
|
11
|
+
|
12
|
+
def perform(environment)
|
13
|
+
operator = "unary_#{@operator}"
|
14
|
+
literal = @operand.perform(environment)
|
15
|
+
literal.send(operator)
|
16
|
+
rescue NoMethodError => e
|
17
|
+
raise e unless e.name.to_s == operator.to_s
|
18
|
+
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sass
|
2
|
+
module Script
|
3
|
+
class Variable # :nodoc:
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def initialize(name)
|
7
|
+
@name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"!#{name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform(environment)
|
15
|
+
(val = environment.var(name)) && (return val)
|
16
|
+
raise SyntaxError.new("Undefined variable: \"!#{name}\".")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/sass/script.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'strscan'
|
2
|
+
require 'sass/script/variable'
|
3
|
+
require 'sass/script/funcall'
|
4
|
+
require 'sass/script/operation'
|
5
|
+
require 'sass/script/literal'
|
6
|
+
require 'sass/script/parser'
|
7
|
+
|
8
|
+
module Sass
|
9
|
+
# This module contains various SassScript-related functionality.
|
10
|
+
module Script
|
11
|
+
# :stopdoc:
|
12
|
+
# The character that begins a variable.
|
13
|
+
VARIABLE_CHAR = ?!
|
14
|
+
|
15
|
+
# The regular expression used to parse variables
|
16
|
+
MATCH = /^!([a-zA-Z_]\w*)\s*((?:\|\|)?=)\s*(.+)/
|
17
|
+
|
18
|
+
# The regular expression used to validate variables without matching
|
19
|
+
VALIDATE = /^![a-zA-Z_]\w*$/
|
20
|
+
|
21
|
+
def self.resolve(value, line, offset, environment)
|
22
|
+
parse(value, line, offset).perform(environment).to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse(value, line, offset, filename = nil)
|
26
|
+
Parser.parse(value, line, offset, filename)
|
27
|
+
rescue Sass::SyntaxError => e
|
28
|
+
if e.message == "SassScript error"
|
29
|
+
e.instance_eval do
|
30
|
+
@message += ": #{value.dump}."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
e.sass_line = line
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
# :startdoc:
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Sass::Tree
|
2
|
+
class AttrNode < Node
|
3
|
+
attr_accessor :name, :value
|
4
|
+
|
5
|
+
def initialize(name, value, options)
|
6
|
+
@name = name
|
7
|
+
@value = value
|
8
|
+
super(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(other)
|
12
|
+
self.class == other.class && name == other.name && value == other.value && super
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s(tabs, parent_name = nil)
|
16
|
+
if value[-1] == ?;
|
17
|
+
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump} (This isn't CSS!).", @line)
|
18
|
+
end
|
19
|
+
real_name = name
|
20
|
+
real_name = "#{parent_name}-#{real_name}" if parent_name
|
21
|
+
|
22
|
+
if value.empty? && children.empty?
|
23
|
+
raise Sass::SyntaxError.new("Invalid attribute: #{declaration.dump}.", @line)
|
24
|
+
end
|
25
|
+
|
26
|
+
join_string = case @style
|
27
|
+
when :compact; ' '
|
28
|
+
when :compressed; ''
|
29
|
+
else "\n"
|
30
|
+
end
|
31
|
+
spaces = ' ' * (tabs - 1)
|
32
|
+
to_return = ''
|
33
|
+
if !value.empty?
|
34
|
+
to_return << "#{spaces}#{real_name}:#{@style == :compressed ? '' : ' '}#{value};#{join_string}"
|
35
|
+
end
|
36
|
+
|
37
|
+
children.each do |kid|
|
38
|
+
to_return << kid.to_s(tabs, real_name) << join_string
|
39
|
+
end
|
40
|
+
|
41
|
+
(@style == :compressed && parent_name) ? to_return : to_return[0...-1]
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def perform!(environment)
|
47
|
+
@name = interpolate(@name, environment)
|
48
|
+
@value = @value.is_a?(String) ? interpolate(@value, environment) : @value.perform(environment).to_s
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def declaration
|
55
|
+
":#{name} #{value}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def invalid_child?(child)
|
59
|
+
if !child.is_a?(AttrNode) && !child.is_a?(CommentNode)
|
60
|
+
"Illegal nesting: Only attributes may be nested beneath attributes."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'sass/tree/node'
|
2
|
+
|
3
|
+
module Sass::Tree
|
4
|
+
class CommentNode < Node
|
5
|
+
attr_accessor :lines
|
6
|
+
attr_accessor :value
|
7
|
+
|
8
|
+
def initialize(value, options)
|
9
|
+
@lines = []
|
10
|
+
@value = value[2..-1].strip
|
11
|
+
super(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def ==(other)
|
15
|
+
self.class == other.class && value == other.value && lines == other.lines
|
16
|
+
end
|
17
|
+
|
18
|
+
def silent?
|
19
|
+
!!@options[:silent]
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s(tabs = 0, parent_name = nil)
|
23
|
+
return if (@style == :compressed || silent?)
|
24
|
+
|
25
|
+
spaces = ' ' * (tabs - 1)
|
26
|
+
spaces + "/* " + ([value] + lines.map {|l| l.text}).
|
27
|
+
map{|l| l.sub(%r{ ?\*/ *$},'')}.join(@style == :compact ? ' ' : "\n#{spaces} * ") + " */"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sass
|
2
|
+
module Tree
|
3
|
+
class DebugNode < Node
|
4
|
+
def initialize(expr, options)
|
5
|
+
@expr = expr
|
6
|
+
super(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def _perform(environment)
|
12
|
+
res = @expr.perform(environment)
|
13
|
+
if filename
|
14
|
+
STDERR.puts "#{filename}:#{line} DEBUG: #{res}"
|
15
|
+
else
|
16
|
+
STDERR.puts "Line #{line} DEBUG: #{res}"
|
17
|
+
end
|
18
|
+
[]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|