drnic-haml 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.yardopts +5 -0
- data/CONTRIBUTING +4 -0
- data/MIT-LICENSE +20 -0
- data/README.md +347 -0
- data/REVISION +1 -0
- data/Rakefile +371 -0
- data/VERSION +1 -0
- data/VERSION_NAME +1 -0
- data/bin/css2sass +7 -0
- data/bin/haml +9 -0
- data/bin/html2haml +7 -0
- data/bin/sass +8 -0
- data/extra/haml-mode.el +663 -0
- data/extra/sass-mode.el +205 -0
- data/extra/update_watch.rb +13 -0
- data/init.rb +8 -0
- data/lib/haml.rb +40 -0
- data/lib/haml/buffer.rb +307 -0
- data/lib/haml/engine.rb +301 -0
- data/lib/haml/error.rb +22 -0
- data/lib/haml/exec.rb +470 -0
- data/lib/haml/filters.rb +341 -0
- data/lib/haml/helpers.rb +560 -0
- data/lib/haml/helpers/action_view_extensions.rb +40 -0
- data/lib/haml/helpers/action_view_mods.rb +176 -0
- data/lib/haml/herb.rb +96 -0
- data/lib/haml/html.rb +308 -0
- data/lib/haml/precompiler.rb +997 -0
- data/lib/haml/shared.rb +78 -0
- data/lib/haml/template.rb +51 -0
- data/lib/haml/template/patch.rb +58 -0
- data/lib/haml/template/plugin.rb +71 -0
- data/lib/haml/util.rb +244 -0
- data/lib/haml/version.rb +64 -0
- data/lib/sass.rb +24 -0
- data/lib/sass/css.rb +423 -0
- data/lib/sass/engine.rb +491 -0
- data/lib/sass/environment.rb +79 -0
- data/lib/sass/error.rb +162 -0
- data/lib/sass/files.rb +133 -0
- data/lib/sass/plugin.rb +170 -0
- data/lib/sass/plugin/merb.rb +57 -0
- data/lib/sass/plugin/rails.rb +23 -0
- data/lib/sass/repl.rb +58 -0
- data/lib/sass/script.rb +55 -0
- data/lib/sass/script/bool.rb +17 -0
- data/lib/sass/script/color.rb +183 -0
- data/lib/sass/script/funcall.rb +50 -0
- data/lib/sass/script/functions.rb +199 -0
- data/lib/sass/script/lexer.rb +191 -0
- data/lib/sass/script/literal.rb +177 -0
- data/lib/sass/script/node.rb +14 -0
- data/lib/sass/script/number.rb +381 -0
- data/lib/sass/script/operation.rb +45 -0
- data/lib/sass/script/parser.rb +222 -0
- data/lib/sass/script/string.rb +12 -0
- data/lib/sass/script/unary_operation.rb +34 -0
- data/lib/sass/script/variable.rb +31 -0
- data/lib/sass/tree/comment_node.rb +84 -0
- data/lib/sass/tree/debug_node.rb +30 -0
- data/lib/sass/tree/directive_node.rb +70 -0
- data/lib/sass/tree/for_node.rb +48 -0
- data/lib/sass/tree/if_node.rb +54 -0
- data/lib/sass/tree/import_node.rb +69 -0
- data/lib/sass/tree/mixin_def_node.rb +29 -0
- data/lib/sass/tree/mixin_node.rb +48 -0
- data/lib/sass/tree/node.rb +252 -0
- data/lib/sass/tree/prop_node.rb +106 -0
- data/lib/sass/tree/root_node.rb +56 -0
- data/lib/sass/tree/rule_node.rb +220 -0
- data/lib/sass/tree/variable_node.rb +34 -0
- data/lib/sass/tree/while_node.rb +31 -0
- data/rails/init.rb +1 -0
- data/test/benchmark.rb +99 -0
- data/test/haml/engine_test.rb +1129 -0
- data/test/haml/helper_test.rb +282 -0
- data/test/haml/html2haml_test.rb +258 -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 +12 -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/spec_test.rb +44 -0
- data/test/haml/template_test.rb +217 -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 +8 -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 +92 -0
- data/test/linked_rails.rb +12 -0
- data/test/sass/css2sass_test.rb +294 -0
- data/test/sass/engine_test.rb +956 -0
- data/test/sass/functions_test.rb +126 -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 +229 -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 +261 -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/bork1.sass +2 -0
- data/test/sass/templates/bork2.sass +2 -0
- data/test/sass/templates/bork3.sass +2 -0
- data/test/sass/templates/compact.sass +17 -0
- data/test/sass/templates/complex.sass +307 -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/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/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 +44 -0
- metadata +298 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
module Sass::Script
|
|
2
|
+
# Methods in this module are accessible from the SassScript context.
|
|
3
|
+
# For example, you can write
|
|
4
|
+
#
|
|
5
|
+
# !color = hsl(120, 100%, 50%)
|
|
6
|
+
#
|
|
7
|
+
# and it will call {Sass::Script::Functions#hsl}.
|
|
8
|
+
#
|
|
9
|
+
# The following functions are provided:
|
|
10
|
+
#
|
|
11
|
+
# \{#hsl}
|
|
12
|
+
# : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
|
|
13
|
+
#
|
|
14
|
+
# \{#percentage}
|
|
15
|
+
# : Converts a unitless number to a percentage.
|
|
16
|
+
#
|
|
17
|
+
# \{#round}
|
|
18
|
+
# : Rounds a number to the nearest whole number.
|
|
19
|
+
#
|
|
20
|
+
# \{#ceil}
|
|
21
|
+
# : Rounds a number up to the nearest whole number.
|
|
22
|
+
#
|
|
23
|
+
# \{#floor}
|
|
24
|
+
# : Rounds a number down to the nearest whole number.
|
|
25
|
+
#
|
|
26
|
+
# \{#abs}
|
|
27
|
+
# : Returns the absolute value of a number.
|
|
28
|
+
#
|
|
29
|
+
# You can add your own functions to this module,
|
|
30
|
+
# but there are a few things to keep in mind.
|
|
31
|
+
# First of all, the arguments passed are {Sass::Script::Literal} objects.
|
|
32
|
+
# Literal objects are also expected to be returned.
|
|
33
|
+
#
|
|
34
|
+
# Second, making Ruby functions accessible from Sass introduces the temptation
|
|
35
|
+
# to do things like database access within stylesheets.
|
|
36
|
+
# This temptation must be resisted.
|
|
37
|
+
# Keep in mind that Sass stylesheets are only compiled once
|
|
38
|
+
# at a somewhat indeterminate time
|
|
39
|
+
# and then left as static CSS files.
|
|
40
|
+
# Any dynamic CSS should be left in `<style>` tags in the HTML.
|
|
41
|
+
#
|
|
42
|
+
# Within one of the functions in this module,
|
|
43
|
+
# methods of {EvaluationContext} can be used.
|
|
44
|
+
module Functions
|
|
45
|
+
# The context in which methods in {Script::Functions} are evaluated.
|
|
46
|
+
# That means that all instance methods of {EvaluationContext}
|
|
47
|
+
# are available to use in functions.
|
|
48
|
+
class EvaluationContext
|
|
49
|
+
include Sass::Script::Functions
|
|
50
|
+
|
|
51
|
+
# The options hash for the {Sass::Engine} that is processing the function call
|
|
52
|
+
#
|
|
53
|
+
# @return [Hash<Symbol, Object>]
|
|
54
|
+
attr_reader :options
|
|
55
|
+
|
|
56
|
+
# @param options [Hash<Symbol, Object>] See \{#options}
|
|
57
|
+
def initialize(options)
|
|
58
|
+
@options = options
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
instance_methods.each { |m| undef_method m unless m.to_s =~ /^__/ }
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# Creates a {Color} object from red, green, and blue values.
|
|
66
|
+
# @param red
|
|
67
|
+
# A number between 0 and 255 inclusive
|
|
68
|
+
# @param green
|
|
69
|
+
# A number between 0 and 255 inclusive
|
|
70
|
+
# @param blue
|
|
71
|
+
# A number between 0 and 255 inclusive
|
|
72
|
+
def rgb(red, green, blue)
|
|
73
|
+
[red.value, green.value, blue.value].each do |v|
|
|
74
|
+
next unless v < 0 || v > 255
|
|
75
|
+
raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
|
|
76
|
+
end
|
|
77
|
+
Color.new([red.value, green.value, blue.value])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Creates a {Color} object from hue, saturation, and lightness
|
|
81
|
+
# as per the CSS3 spec (http://www.w3.org/TR/css3-color/#hsl-color).
|
|
82
|
+
#
|
|
83
|
+
# @param hue [Number] The hue of the color.
|
|
84
|
+
# Should be between 0 and 360 degrees, inclusive
|
|
85
|
+
# @param saturation [Number] The saturation of the color.
|
|
86
|
+
# Must be between `0%` and `100%`, inclusive
|
|
87
|
+
# @param lightness [Number] The lightness of the color.
|
|
88
|
+
# Must be between `0%` and `100%`, inclusive
|
|
89
|
+
# @return [Color] The resulting color
|
|
90
|
+
# @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
|
|
91
|
+
def hsl(hue, saturation, lightness)
|
|
92
|
+
original_s = saturation
|
|
93
|
+
original_l = lightness
|
|
94
|
+
# This algorithm is from http://www.w3.org/TR/css3-color#hsl-color
|
|
95
|
+
h, s, l = [hue, saturation, lightness].map { |a| a.value }
|
|
96
|
+
raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") if s < 0 || s > 100
|
|
97
|
+
raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") if l < 0 || l > 100
|
|
98
|
+
|
|
99
|
+
h = (h % 360) / 360.0
|
|
100
|
+
s /= 100.0
|
|
101
|
+
l /= 100.0
|
|
102
|
+
|
|
103
|
+
m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
|
|
104
|
+
m1 = l * 2 - m2
|
|
105
|
+
Color.new([hue_to_rgb(m1, m2, h + 1.0/3),
|
|
106
|
+
hue_to_rgb(m1, m2, h),
|
|
107
|
+
hue_to_rgb(m1, m2, h - 1.0/3)].map { |c| (c * 0xff).round })
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Converts a decimal number to a percentage.
|
|
111
|
+
# For example:
|
|
112
|
+
#
|
|
113
|
+
# percentage(100px / 50px) => 200%
|
|
114
|
+
#
|
|
115
|
+
# @param value [Number] The decimal number to convert to a percentage
|
|
116
|
+
# @return [Number] The percentage
|
|
117
|
+
# @raise [ArgumentError] If `value` isn't a unitless number
|
|
118
|
+
def percentage(value)
|
|
119
|
+
unless value.is_a?(Sass::Script::Number) && value.unitless?
|
|
120
|
+
raise ArgumentError.new("#{value} is not a unitless number")
|
|
121
|
+
end
|
|
122
|
+
Sass::Script::Number.new(value.value * 100, ['%'])
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Rounds a number to the nearest whole number.
|
|
126
|
+
# For example:
|
|
127
|
+
#
|
|
128
|
+
# round(10.4px) => 10px
|
|
129
|
+
# round(10.6px) => 11px
|
|
130
|
+
#
|
|
131
|
+
# @param value [Number] The number
|
|
132
|
+
# @return [Number] The rounded number
|
|
133
|
+
# @raise [Sass::SyntaxError] if `value` isn't a number
|
|
134
|
+
def round(value)
|
|
135
|
+
numeric_transformation(value) {|n| n.round}
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Rounds a number up to the nearest whole number.
|
|
139
|
+
# For example:
|
|
140
|
+
#
|
|
141
|
+
# ciel(10.4px) => 11px
|
|
142
|
+
# ciel(10.6px) => 11px
|
|
143
|
+
#
|
|
144
|
+
# @param value [Number] The number
|
|
145
|
+
# @return [Number] The rounded number
|
|
146
|
+
# @raise [Sass::SyntaxError] if `value` isn't a number
|
|
147
|
+
def ceil(value)
|
|
148
|
+
numeric_transformation(value) {|n| n.ceil}
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Rounds down to the nearest whole number.
|
|
152
|
+
# For example:
|
|
153
|
+
#
|
|
154
|
+
# floor(10.4px) => 10px
|
|
155
|
+
# floor(10.6px) => 10px
|
|
156
|
+
#
|
|
157
|
+
# @param value [Number] The number
|
|
158
|
+
# @return [Number] The rounded number
|
|
159
|
+
# @raise [Sass::SyntaxError] if `value` isn't a number
|
|
160
|
+
def floor(value)
|
|
161
|
+
numeric_transformation(value) {|n| n.floor}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Finds the absolute value of a number.
|
|
165
|
+
# For example:
|
|
166
|
+
#
|
|
167
|
+
# abs(10px) => 10px
|
|
168
|
+
# abs(-10px) => 10px
|
|
169
|
+
#
|
|
170
|
+
# @param value [Number] The number
|
|
171
|
+
# @return [Number] The absolute value
|
|
172
|
+
# @raise [Sass::SyntaxError] if `value` isn't a number
|
|
173
|
+
def abs(value)
|
|
174
|
+
numeric_transformation(value) {|n| n.abs}
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
private
|
|
178
|
+
|
|
179
|
+
# This method implements the pattern of transforming a numeric value into
|
|
180
|
+
# another numeric value with the same units.
|
|
181
|
+
# It yields a number to a block to perform the operation and return a number
|
|
182
|
+
def numeric_transformation(value)
|
|
183
|
+
unless value.is_a?(Sass::Script::Number)
|
|
184
|
+
calling_function = caller.first.scan(/`([^']+)'/).first.first
|
|
185
|
+
raise Sass::SyntaxError.new("#{value} is not a number for `#{calling_function}'")
|
|
186
|
+
end
|
|
187
|
+
Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def hue_to_rgb(m1, m2, h)
|
|
191
|
+
h += 1 if h < 0
|
|
192
|
+
h -= 1 if h > 1
|
|
193
|
+
return m1 + (m2 - m1) * h * 6 if h * 6 < 1
|
|
194
|
+
return m2 if h * 2 < 1
|
|
195
|
+
return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
|
|
196
|
+
return m1
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'strscan'
|
|
2
|
+
|
|
3
|
+
module Sass
|
|
4
|
+
module Script
|
|
5
|
+
# The lexical analyzer for SassScript.
|
|
6
|
+
# It takes a raw string and converts it to individual tokens
|
|
7
|
+
# that are easier to parse.
|
|
8
|
+
class Lexer
|
|
9
|
+
# A struct containing information about an individual token.
|
|
10
|
+
#
|
|
11
|
+
# `type`: [{Symbol}]
|
|
12
|
+
# : The type of token.
|
|
13
|
+
#
|
|
14
|
+
# `value`: [{Object}]
|
|
15
|
+
# : The Ruby object corresponding to the value of the token.
|
|
16
|
+
#
|
|
17
|
+
# `line`: [{Fixnum}]
|
|
18
|
+
# : The line of the source file on which the token appears.
|
|
19
|
+
#
|
|
20
|
+
# `offset`: [{Fixnum}]
|
|
21
|
+
# : The number of bytes into the line the SassScript token appeared.
|
|
22
|
+
Token = Struct.new(:type, :value, :line, :offset)
|
|
23
|
+
|
|
24
|
+
# A hash from operator strings to the corresponding token types.
|
|
25
|
+
OPERATORS = {
|
|
26
|
+
'+' => :plus,
|
|
27
|
+
'-' => :minus,
|
|
28
|
+
'*' => :times,
|
|
29
|
+
'/' => :div,
|
|
30
|
+
'%' => :mod,
|
|
31
|
+
'=' => :single_eq,
|
|
32
|
+
'(' => :lparen,
|
|
33
|
+
')' => :rparen,
|
|
34
|
+
',' => :comma,
|
|
35
|
+
'and' => :and,
|
|
36
|
+
'or' => :or,
|
|
37
|
+
'not' => :not,
|
|
38
|
+
'==' => :eq,
|
|
39
|
+
'!=' => :neq,
|
|
40
|
+
'>=' => :gte,
|
|
41
|
+
'<=' => :lte,
|
|
42
|
+
'>' => :gt,
|
|
43
|
+
'<' => :lt,
|
|
44
|
+
'#{' => :begin_interpolation,
|
|
45
|
+
'}' => :end_interpolation,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# A list of operator strings ordered with longer names first
|
|
49
|
+
# so that `>` and `<` don't clobber `>=` and `<=`.
|
|
50
|
+
OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
|
|
51
|
+
|
|
52
|
+
# A hash of regular expressions that are used for tokenizing.
|
|
53
|
+
REGULAR_EXPRESSIONS = {
|
|
54
|
+
:whitespace => /\s*/,
|
|
55
|
+
:variable => /!(\w+)/,
|
|
56
|
+
:ident => /(\\.|\#\{|[^\s\\+\-*\/%(),=!])+/,
|
|
57
|
+
:string_end => /((?:\\.|\#[^{]|[^"\\#])*)(?:"|(?=#\{))/,
|
|
58
|
+
:number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
|
|
59
|
+
:color => /\##{"([0-9a-fA-F]{1,2})" * 3}|(#{Color::HTML4_COLORS.keys.join("|")})/,
|
|
60
|
+
:bool => /(true|false)\b/,
|
|
61
|
+
:op => %r{(#{Regexp.union(*OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + (s =~ /\w$/ ? '(?:\b|$)' : ''))})})}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# @param str [String, StringScanner] The source text to lex
|
|
65
|
+
# @param line [Fixnum] The line on which the SassScript appears.
|
|
66
|
+
# Used for error reporting
|
|
67
|
+
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
|
|
68
|
+
# Used for error reporting
|
|
69
|
+
def initialize(str, line, offset, filename)
|
|
70
|
+
@scanner = str.is_a?(StringScanner) ? str : StringScanner.new(str)
|
|
71
|
+
@line = line
|
|
72
|
+
@offset = offset
|
|
73
|
+
@filename = filename
|
|
74
|
+
@prev = nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Moves the lexer forward one token.
|
|
78
|
+
#
|
|
79
|
+
# @return [Token] The token that was moved past
|
|
80
|
+
def next
|
|
81
|
+
@tok ||= read_token
|
|
82
|
+
@tok, tok = nil, @tok
|
|
83
|
+
@prev = tok
|
|
84
|
+
return tok
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Returns the next token without moving the lexer forward.
|
|
88
|
+
#
|
|
89
|
+
# @return [Token] The next token
|
|
90
|
+
def peek
|
|
91
|
+
@tok ||= read_token
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# @return [Boolean] Whether or not there's more source text to lex.
|
|
95
|
+
def done?
|
|
96
|
+
whitespace unless after_interpolation?
|
|
97
|
+
@scanner.eos? && @tok.nil?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def read_token
|
|
103
|
+
return if done?
|
|
104
|
+
|
|
105
|
+
value = token
|
|
106
|
+
unless value
|
|
107
|
+
raise SyntaxError.new("Syntax error in '#{@scanner.string}' at character #{current_position}.")
|
|
108
|
+
end
|
|
109
|
+
Token.new(value.first, value.last, @line, last_match_position)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def whitespace
|
|
113
|
+
@scanner.scan(REGULAR_EXPRESSIONS[:whitespace])
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def token
|
|
117
|
+
return string('') if after_interpolation?
|
|
118
|
+
variable || string || number || color || bool || op || ident
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def variable
|
|
122
|
+
return unless @scanner.scan(REGULAR_EXPRESSIONS[:variable])
|
|
123
|
+
[:const, @scanner[1]]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def ident
|
|
127
|
+
return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:ident])
|
|
128
|
+
[:ident, s.gsub(/\\(.)/, '\1')]
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def string(start_char = '"')
|
|
132
|
+
return unless @scanner.scan(/#{start_char}#{REGULAR_EXPRESSIONS[:string_end]}/)
|
|
133
|
+
[:string, Script::String.new(@scanner[1].gsub(/\\([^0-9a-f])/, '\1').gsub(/\\([0-9a-f]{1,4})/, "\\\\\\1"))]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def begin_interpolation
|
|
137
|
+
@scanner.scan
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def number
|
|
141
|
+
return unless @scanner.scan(REGULAR_EXPRESSIONS[:number])
|
|
142
|
+
value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
|
|
143
|
+
value = -value if @scanner[1]
|
|
144
|
+
[:number, Script::Number.new(value, Array(@scanner[4]))]
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def color
|
|
148
|
+
return unless @scanner.scan(REGULAR_EXPRESSIONS[:color])
|
|
149
|
+
value = if @scanner[4]
|
|
150
|
+
color = Color::HTML4_COLORS[@scanner[4].downcase]
|
|
151
|
+
else
|
|
152
|
+
(1..3).map {|i| @scanner[i]}.map {|num| num.ljust(2, num).to_i(16)}
|
|
153
|
+
end
|
|
154
|
+
[:color, Script::Color.new(value)]
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def bool
|
|
158
|
+
return unless s = @scanner.scan(REGULAR_EXPRESSIONS[:bool])
|
|
159
|
+
[:bool, Script::Bool.new(s == 'true')]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def op
|
|
163
|
+
prev_chr = @scanner.string[@scanner.pos - 1].chr
|
|
164
|
+
return unless op = @scanner.scan(REGULAR_EXPRESSIONS[:op])
|
|
165
|
+
if @prev && op == '-' && prev_chr !~ /\s/ &&
|
|
166
|
+
[:bool, :ident, :const].include?(@prev.type)
|
|
167
|
+
warn(<<END)
|
|
168
|
+
DEPRECATION WARNING:
|
|
169
|
+
On line #{@line}, character #{last_match_position}#{" of '#{@filename}'" if @filename}
|
|
170
|
+
- will be allowed as part of variable names in version 2.4.
|
|
171
|
+
Please add whitespace to separate it from the previous token.
|
|
172
|
+
END
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
[OPERATORS[op]]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def current_position
|
|
179
|
+
@offset + @scanner.pos + 1
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def last_match_position
|
|
183
|
+
current_position - @scanner.matched_size
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def after_interpolation?
|
|
187
|
+
@prev && @prev.type == :end_interpolation
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
module Sass::Script
|
|
2
|
+
# The abstract superclass for SassScript objects.
|
|
3
|
+
#
|
|
4
|
+
# Many of these methods, especially the ones that correspond to SassScript operations,
|
|
5
|
+
# are designed to be overridden by subclasses which may change the semantics somewhat.
|
|
6
|
+
# The operations listed here are just the defaults.
|
|
7
|
+
class Literal < Node
|
|
8
|
+
require 'sass/script/string'
|
|
9
|
+
require 'sass/script/number'
|
|
10
|
+
require 'sass/script/color'
|
|
11
|
+
require 'sass/script/bool'
|
|
12
|
+
|
|
13
|
+
# Returns the Ruby value of the literal.
|
|
14
|
+
# The type of this value varies based on the subclass.
|
|
15
|
+
#
|
|
16
|
+
# @return [Object]
|
|
17
|
+
attr_reader :value
|
|
18
|
+
|
|
19
|
+
# Creates a new literal.
|
|
20
|
+
#
|
|
21
|
+
# @param value [Object] The object for \{#value}
|
|
22
|
+
def initialize(value = nil)
|
|
23
|
+
@value = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Evaluates the literal.
|
|
27
|
+
#
|
|
28
|
+
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
|
|
29
|
+
# @return [Literal] This literal
|
|
30
|
+
def perform(environment)
|
|
31
|
+
self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# The SassScript `and` operation.
|
|
35
|
+
#
|
|
36
|
+
# @param other [Literal] The right-hand side of the operator
|
|
37
|
+
# @return [Literal] The result of a logical and:
|
|
38
|
+
# `other` if this literal isn't a false {Bool},
|
|
39
|
+
# and this literal otherwise
|
|
40
|
+
def and(other)
|
|
41
|
+
to_bool ? other : self
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The SassScript `or` operation.
|
|
45
|
+
#
|
|
46
|
+
# @param other [Literal] The right-hand side of the operator
|
|
47
|
+
# @return [Literal] The result of the logical or:
|
|
48
|
+
# this literal if it isn't a false {Bool},
|
|
49
|
+
# and `other` otherwise
|
|
50
|
+
def or(other)
|
|
51
|
+
to_bool ? self : other
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# The SassScript `==` operation.
|
|
55
|
+
# **Note that this returns a {Sass::Script::Bool} object,
|
|
56
|
+
# not a Ruby boolean**.
|
|
57
|
+
#
|
|
58
|
+
# @param other [Literal] The right-hand side of the operator
|
|
59
|
+
# @return [Bool] True if this literal is the same as the other,
|
|
60
|
+
# false otherwise
|
|
61
|
+
def eq(other)
|
|
62
|
+
Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# The SassScript `!=` operation.
|
|
66
|
+
# **Note that this returns a {Sass::Script::Bool} object,
|
|
67
|
+
# not a Ruby boolean**.
|
|
68
|
+
#
|
|
69
|
+
# @param other [Literal] The right-hand side of the operator
|
|
70
|
+
# @return [Bool] False if this literal is the same as the other,
|
|
71
|
+
# true otherwise
|
|
72
|
+
def neq(other)
|
|
73
|
+
Sass::Script::Bool.new(!eq(other).to_bool)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The SassScript `==` operation.
|
|
77
|
+
# **Note that this returns a {Sass::Script::Bool} object,
|
|
78
|
+
# not a Ruby boolean**.
|
|
79
|
+
#
|
|
80
|
+
# @param other [Literal] The right-hand side of the operator
|
|
81
|
+
# @return [Bool] True if this literal is the same as the other,
|
|
82
|
+
# false otherwise
|
|
83
|
+
def unary_not
|
|
84
|
+
Sass::Script::Bool.new(!to_bool)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The SassScript default operation (e.g. `!a !b`, `"foo" "bar"`).
|
|
88
|
+
#
|
|
89
|
+
# @param other [Literal] The right-hand side of the operator
|
|
90
|
+
# @return [Script::String] A string containing both literals
|
|
91
|
+
# separated by a space
|
|
92
|
+
def concat(other)
|
|
93
|
+
Sass::Script::String.new("#{self.to_s} #{other.to_s}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The SassScript `,` operation (e.g. `!a, !b`, `"foo", "bar"`).
|
|
97
|
+
#
|
|
98
|
+
# @param other [Literal] The right-hand side of the operator
|
|
99
|
+
# @return [Script::String] A string containing both literals
|
|
100
|
+
# separated by `", "`
|
|
101
|
+
def comma(other)
|
|
102
|
+
Sass::Script::String.new("#{self.to_s}, #{other.to_s}")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# The SassScript `+` operation.
|
|
106
|
+
#
|
|
107
|
+
# @param other [Literal] The right-hand side of the operator
|
|
108
|
+
# @return [Script::String] A string containing both literals
|
|
109
|
+
# without any separation
|
|
110
|
+
def plus(other)
|
|
111
|
+
Sass::Script::String.new(self.to_s + other.to_s)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# The SassScript `-` operation.
|
|
115
|
+
#
|
|
116
|
+
# @param other [Literal] The right-hand side of the operator
|
|
117
|
+
# @return [Script::String] A string containing both literals
|
|
118
|
+
# separated by `"-"`
|
|
119
|
+
def minus(other)
|
|
120
|
+
Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# The SassScript `/` operation.
|
|
124
|
+
#
|
|
125
|
+
# @param other [Literal] The right-hand side of the operator
|
|
126
|
+
# @return [Script::String] A string containing both literals
|
|
127
|
+
# separated by `"/"`
|
|
128
|
+
def div(other)
|
|
129
|
+
Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# The SassScript unary `-` operation (e.g. `-!a`).
|
|
133
|
+
#
|
|
134
|
+
# @param other [Literal] The right-hand side of the operator
|
|
135
|
+
# @return [Script::String] A string containing the literal
|
|
136
|
+
# preceded by `"-"`
|
|
137
|
+
def unary_minus
|
|
138
|
+
Sass::Script::String.new("-#{self.to_s}")
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# The SassScript unary `/` operation (e.g. `/!a`).
|
|
142
|
+
#
|
|
143
|
+
# @param other [Literal] The right-hand side of the operator
|
|
144
|
+
# @return [Script::String] A string containing the literal
|
|
145
|
+
# preceded by `"/"`
|
|
146
|
+
def unary_div
|
|
147
|
+
Sass::Script::String.new("/#{self.to_s}")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @return [String] A readable representation of the literal
|
|
151
|
+
def inspect
|
|
152
|
+
value.inspect
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# @return [Boolean] `true` (the Ruby boolean value)
|
|
156
|
+
def to_bool
|
|
157
|
+
true
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Compares this object with another.
|
|
161
|
+
#
|
|
162
|
+
# @param other [Object] The object to compare with
|
|
163
|
+
# @return [Boolean] Whether or not this literal is equivalent to `other`
|
|
164
|
+
def ==(other)
|
|
165
|
+
eq(other).to_bool
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# @return [Fixnum] The integer value of this literal
|
|
169
|
+
# @raise [Sass::SyntaxError] if this literal isn't an integer
|
|
170
|
+
def to_i
|
|
171
|
+
raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# @raise [Sass::SyntaxError] if this literal isn't an integer
|
|
175
|
+
def assert_int!; to_i; end
|
|
176
|
+
end
|
|
177
|
+
end
|